/*!
* This program is free software; you can redistribute it and/or modify it under the
* terms of the GNU Lesser General Public License, version 2.1 as published by the Free Software
* Foundation.
*
* You should have received a copy of the GNU Lesser General Public License along with this
* program; if not, you can obtain a copy at http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html
* or from the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*
* This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
* without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
* See the GNU Lesser General Public License for more details.
*
* Copyright (c) 2002-2013 Pentaho Corporation.. All rights reserved.
*/
package org.pentaho.platform.dataaccess.datasource.api.resources;
import static javax.ws.rs.core.MediaType.APPLICATION_JSON;
import static javax.ws.rs.core.MediaType.APPLICATION_XML;
import java.util.ArrayList;
import java.util.List;
import javax.ws.rs.Consumes;
import javax.ws.rs.DELETE;
import javax.ws.rs.GET;
import javax.ws.rs.PUT;
import javax.ws.rs.Path;
import javax.ws.rs.PathParam;
import javax.ws.rs.Produces;
import javax.ws.rs.WebApplicationException;
import javax.ws.rs.core.Response;
import org.apache.commons.lang.StringUtils;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.codehaus.enunciate.jaxrs.ResponseCode;
import org.codehaus.enunciate.jaxrs.StatusCodes;
import org.pentaho.database.model.DatabaseConnection;
import org.pentaho.database.model.IDatabaseConnection;
import org.pentaho.platform.api.engine.PentahoAccessControlException;
import org.pentaho.platform.dataaccess.datasource.api.DatasourceService;
import org.pentaho.platform.dataaccess.datasource.wizard.service.ConnectionServiceException;
import org.pentaho.platform.dataaccess.datasource.wizard.service.impl.ConnectionServiceImpl;
import org.pentaho.platform.web.http.api.resources.JaxbList;
/**
* This service provides methods for listing, creating, downloading, uploading, and removal of JDBC data sources.
*/
@Path( "/data-access/api/datasource/jdbc/connection" )
public class JDBCDatasourceResource {
protected ConnectionServiceImpl service;
private static final Log logger = LogFactory.getLog( JDBCDatasourceResource.class );
public JDBCDatasourceResource() {
service = new ConnectionServiceImpl();
}
/**
* Remove the JDBC data source for a given JDBC ID.
*
* <p><b>Example Request:</b><br />
* DELETE pentaho/plugin/data-access/api/datasource/jdbc/connection/TestDataSourceResource
* </p>
*
* @param name The name of the JDBC datasource to remove
*
* @return A 200 response code representing the successful removal of the JDBC datasource.
*
* <p><b>Example Response:</b></p>
* <pre function="syntax.xml">
* This response does not contain data.
* </pre>
*/
@DELETE
@Path( "/{name : .+}" )
@StatusCodes( {
@ResponseCode( code = 200, condition = "JDBC datasource removed successfully." ),
@ResponseCode( code = 304,
condition = "User is not authorized to remove the JDBC datasource or the connection does not exist." ),
@ResponseCode( code = 500, condition = "An unexected error occurred while deleting the JDBC datasource." )
} )
public Response deleteConnection( @PathParam( "name" ) String name ) {
try {
boolean success = service.deleteConnection( name );
if ( success ) {
return buildOkResponse();
} else {
return buildNotModifiedResponse();
}
} catch ( Throwable t ) {
return buildServerErrorResponse();
}
}
/**
* Get a list of JDBC datasource IDs.
*
* <p><b>Example Request:</b><br />
* GET pentaho/plugin/data-access/api/datasource/jdbc/connection
* </p>
*
* @return A list of JDBC datasource IDs.
*
* <p><b>Example Response:</b></p>
* <pre function="syntax.xml">
* {
* "Item": [
* {
* "@type": "xs:string",
* "$": "AgileBI"
* },
* {
* "@type": "xs:string",
* "$": "Audit"
* },
* {
* "@type": "xs:string",
* "$": "SampleData"
* },
* {
* "@type": "xs:string",
* "$": "TestDataSourceResource"
* },
* {
* "@type": "xs:string",
* "$": "baseball connection"
* },
* {
* "@type": "xs:string",
* "$": "baseball connection"
* },
* {
* "@type": "xs:string",
* "$": "live_logging_info"
* },
* {
* "@type": "xs:string",
* "$": "pentaho_operations_mart"
* }
* ]
* }
* </pre>
*/
@GET
@Path( "/" )
@Produces( { APPLICATION_JSON, APPLICATION_XML } )
@StatusCodes( {
@ResponseCode( code = 200, condition = "Successfully retrieved the list of JDBC datasource IDs" ),
@ResponseCode( code = 500, condition = "Internal error retrieving JDBC datasource IDs" )
} )
public JaxbList<String> getConnectionIDs() {
List<String> connStrList = new ArrayList<String>();
try {
List<IDatabaseConnection> conns = service.getConnections();
for ( IDatabaseConnection conn : conns ) {
conn.setPassword( null );
connStrList.add( conn.getName() );
}
} catch ( ConnectionServiceException e ) {
logger.error( "Error " + e.getMessage() );
throw new WebApplicationException( Response.Status.INTERNAL_SERVER_ERROR );
}
JaxbList<String> connections = new JaxbList<String>( connStrList );
return connections;
}
/**
* Export a JDBC datasource connection.
*
* <p><b>Example Request:</b><br />
* GET pentaho/plugin/data-access/api/datasource/jdbc/connection/TestDataSourceResource
* </p>
*
* @param name The name of the JDBC datasource to retrieve
*
* @return A Response object containing the JDBC connection in XML or JSON form.
*
* <p><b>Example Response:</b></p>
* <pre function="syntax.xml">
* {
* "SQLServerInstance": null,
* "accessType": "NATIVE",
* "accessTypeValue": "NATIVE",
* "attributes": {
* "PORT_NUMBER": "9001"
* },
* "changed": false,
* "connectSql": "",
* "connectionPoolingProperties": {},
* "dataTablespace": "",
* "databaseName": "SampleData",
* "databasePort": "9001",
* "databaseType": {
* "defaultDatabasePort": 9001,
* "extraOptionsHelpUrl": "http://hsqldb.sourceforge.net/doc/guide/ch04.html#N109DA",
* "name": "Hypersonic",
* "shortName": "HYPERSONIC"
* },
* "extraOptions": {},
* "forcingIdentifiersToLowerCase": false,
* "forcingIdentifiersToUpperCase": false,
* "hostname": "localhost",
* "id": "00ac4db3-7567-4019-8917-1b6f512ee162",
* "indexTablespace": "",
* "informixServername": "",
* "initialPoolSize": 0,
* "maximumPoolSize": 0,
* "name": "TestDataSourceResource",
* "partitioned": false,
* "password": "password",
* "quoteAllFields": false,
* "streamingResults": false,
* "username": "pentaho_user",
* "usingConnectionPool": true,
* "usingDoubleDecimalAsSchemaTableSeparator": false
* }
* </pre>
*/
@GET
@Path( "/{name : .+}" )
@Produces( { APPLICATION_JSON, APPLICATION_XML } )
@StatusCodes( {
@ResponseCode( code = 200, condition = "Successfully retrieved the JDBC datasource" ),
@ResponseCode( code = 500, condition = "An error occurred retrieving the JDBC datasource" )
} )
public Response getConnection( @PathParam( "name" ) String name ) {
try {
return buildOkResponse( service.getConnectionByName( name ) );
} catch ( ConnectionServiceException e ) {
logger.error( "Error " + e.getMessage() );
return buildServerErrorResponse();
}
}
/**
* Add or update a JDBC datasource connection
*
* <p><b>Example Request:</b><br />
* PUT pentaho/plugin/data-access/api/datasource/jdbc/connection/TestDatasource
* </p>
* <br /><b>POST data:</b>
* <pre function="syntax.xml">
* {
* "changed": true,
* "usingConnectionPool": true,
* "connectSql": "",
* "databaseName": "SampleData",
* "databasePort": "9001",
* "hostname": "localhost",
* "name": "TestDataSourceResource",
* "password": "password",
* "username": "pentaho_user",
* "attributes": {},
* "connectionPoolingProperties": {},
* "extraOptions": {},
* "accessType": "NATIVE",
* "databaseType": {
* "defaultDatabasePort": 9001,
* "extraOptionsHelpUrl": "http://hsqldb.sourceforge.net/doc/guide/ch04.html#N109DA",
* "name": "Hypersonic",
* "shortName": "HYPERSONIC",
* "supportedAccessTypes": [
* "NATIVE",
* "ODBC",
* "JNDI"
* ]
* }
* }
* </pre>
* </p>
*
* @param connection A DatabaseConnection in JSON representation
*
* @return A jax-rs Response object with the appropriate status code, header, and body.
*
* <p><b>Example Response:</b></p>
* <pre function="syntax.xml">
* This response does not contain data.
* </pre>
*/
@PUT
@Path( "/{connectionId : .+}" )
@Consumes( { APPLICATION_JSON } )
@StatusCodes( {
@ResponseCode( code = 200, condition = "JDBC datasource added successfully." ),
@ResponseCode( code = 403, condition = "User is not authorized to add JDBC datasources." ),
@ResponseCode( code = 304, condition = "Datasource was not modified" ),
@ResponseCode( code = 500, condition = "An unexected error occurred while adding the JDBC datasource." )
} )
public Response addOrUpdate( @PathParam( "connectionId" ) String connectionName, DatabaseConnection connection ) {
try {
validateAccess();
// Prefer the path name over the one in the DTO object
connection.setId( connectionName );
IDatabaseConnection savedConn = null;
try {
savedConn = service.getConnectionByName( connectionName );
} catch ( ConnectionServiceException e ){
// unfortunatley getConnectionById throws an exception not returning null when the conneciton is not present.
} catch ( NullPointerException e){
// unfortunatley getConnectionById throws an exception not returning null when the conneciton is not present.
}
boolean success = false;
if( savedConn != null ) {
if ( StringUtils.isBlank( connection.getPassword() ) ) {
connection.setPassword( savedConn.getPassword() );
}
connection.setId( savedConn.getId() );
success = service.updateConnection( connection );
} else {
success = service.addConnection( connection );
}
if ( success ) {
return buildOkResponse();
} else {
return buildNotModifiedResponse();
}
} catch ( PentahoAccessControlException t ) {
throw new WebApplicationException( Response.Status.UNAUTHORIZED );
} catch ( Throwable t ){
logger.error( "Error " + t.getMessage() );
return buildServerErrorResponse();
}
}
protected Response buildOkResponse() {
return Response.ok().build();
}
protected Response buildOkResponse( IDatabaseConnection connection ) {
return Response.ok( connection ).build();
}
protected Response buildNotModifiedResponse() {
return Response.notModified().build();
}
protected Response buildServerErrorResponse() {
return Response.serverError().build();
}
protected void validateAccess() throws PentahoAccessControlException {
DatasourceService.validateAccess();
}
}