/**
* Copyright (c) 2009-2011 VMware, Inc. All Rights Reserved.
*
* Licensed 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 com.springsource.insight.plugin.jdbc;
import java.net.URI;
import java.net.URISyntaxException;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.List;
import com.springsource.insight.intercept.color.ColorManager;
import com.springsource.insight.intercept.operation.Operation;
import com.springsource.insight.intercept.operation.OperationFields;
import com.springsource.insight.intercept.operation.OperationType;
import com.springsource.insight.intercept.topology.AbstractExternalResourceAnalyzer;
import com.springsource.insight.intercept.topology.ExternalResourceDescriptor;
import com.springsource.insight.intercept.topology.ExternalResourceType;
import com.springsource.insight.intercept.topology.MD5NameGenerator;
import com.springsource.insight.intercept.trace.Frame;
import com.springsource.insight.intercept.trace.Trace;
import com.springsource.insight.plugin.jdbc.parser.DatabaseType;
import com.springsource.insight.plugin.jdbc.parser.JdbcUrlMetaData;
import com.springsource.insight.util.ListUtil;
import com.springsource.insight.util.StringUtil;
/**
* Locates the JDBC URI of the database from a trace and locates all the external
* resources represented. The only guarantee is that the resource names
* returned will be unique.
* <p/>
* This class will /try/ to make that unique name a uri, and additionally return
* useful information about the host and port of the remote resource, but not all
* jdbc url formats for every database are supported, and not all drivers /can/
* be supported.
* <p/>
* Additional parsers are very welcome.
*/
public abstract class DatabaseJDBCURIAnalyzer extends AbstractExternalResourceAnalyzer {
protected DatabaseJDBCURIAnalyzer(OperationType type) {
super(type);
}
public Collection<ExternalResourceDescriptor> locateExternalResourceName(Trace trace, Collection<Frame> dbFrames) {
if (ListUtil.size(dbFrames) <= 0) {
return Collections.emptyList();
}
List<ExternalResourceDescriptor> dbDescriptors = new ArrayList<ExternalResourceDescriptor>(dbFrames.size());
for (Frame dbFrame : dbFrames) {
Operation op = dbFrame.getOperation();
String uri = op.get(OperationFields.CONNECTION_URL, String.class);
if (StringUtil.isEmpty(uri)) {
continue;
}
dbDescriptors.addAll(extractMeaningfulNames(dbFrame, uri));
}
return dbDescriptors;
}
/**
* The spec only defines a jdbc url as "jdbc:[vendor].*"
* so we try several methods for extracting useful information, some of which
* actually work.
*/
public List<ExternalResourceDescriptor> extractMeaningfulNames(Frame frame, String connectionString) {
// hope some parser recognizes the string
List<ExternalResourceDescriptor> parserRecognizedDescriptors = getParserRecognizedDescriptors(frame, connectionString);
if (ListUtil.size(parserRecognizedDescriptors) > 0) {
return parserRecognizedDescriptors;
}
return getFallbackDescriptor(frame, connectionString);
}
static List<ExternalResourceDescriptor> getFallbackDescriptor(Frame frame, String connectionString) {
String workingConnectionString = connectionString.replaceFirst("jdbc:", "");
int indexOfFirstColon = workingConnectionString.indexOf(':');
if (indexOfFirstColon <= 0) {
return Collections.emptyList();
}
String jdbcScheme = workingConnectionString.substring(0, indexOfFirstColon);
String host = null;
String path = null;
int port = -1;
// Try to parse the string as a real uri
URI uri = extractURI(workingConnectionString);
if (uri != null) {
host = uri.getHost();
port = uri.getPort();
path = uri.getPath();
}
String toHash = jdbcScheme + (path==null ? "" : path) + host + port;
String jdbcHash = MD5NameGenerator.getName(toHash);
ColorManager colorManager = ColorManager.getInstance();
Operation op = frame.getOperation();
String color = colorManager.getColor(op);
// for Non-URI based and special cases the host and port remain default
ExternalResourceDescriptor hashed =
new ExternalResourceDescriptor(frame, jdbcScheme + ":1:" + jdbcHash, "", ExternalResourceType.DATABASE.name(), jdbcScheme, host, port, color, false);
return Collections.singletonList(hashed);
}
static List<ExternalResourceDescriptor> getParserRecognizedDescriptors(Frame frame, String connectionString) {
Collection<? extends JdbcUrlMetaData> urlMetaDataList = DatabaseType.parse(connectionString);
if (ListUtil.size(urlMetaDataList) <= 0) {
return Collections.emptyList();
}
List<ExternalResourceDescriptor> externalResourceDescriptors = new ArrayList<ExternalResourceDescriptor>(urlMetaDataList.size());
int instance = 1;
ColorManager colorManager = ColorManager.getInstance();
Operation op = frame.getOperation();
String color = colorManager.getColor(op);
for (JdbcUrlMetaData urlMetaData : urlMetaDataList) {
String databaseName = urlMetaData.getDatabaseName();
String vendor = urlMetaData.getVendorName();
String host = urlMetaData.getHost();
int port = urlMetaData.getPort();
String nameToHash = vendor + (databaseName == null ? "" : databaseName) + host + port;
String jdbcHash = MD5NameGenerator.getName(nameToHash);
ExternalResourceDescriptor descriptor = new ExternalResourceDescriptor(frame,
vendor + ":" + instance + ":" + jdbcHash,
databaseName,
ExternalResourceType.DATABASE.name(),
vendor,
host,
port,
color, false);
externalResourceDescriptors.add(descriptor);
//using the same instance index as we're assuming no more than one parser will ever succeed in parsing the same url
instance++;
}
return externalResourceDescriptors;
}
/*
* Try to pull a uri out of the jdbc url. If no uri is to be found, return null
*/
static URI extractURI(String possibleURI) {
for (String workingName = possibleURI; StringUtil.getSafeLength(workingName) > 0; ) {
int colonPos = workingName.indexOf(':');
if (colonPos < 0) {
break;
}
try {
// extract a host/port if we can
URI uri = new URI(workingName);
if (uri.getHost() != null) {
return uri;
}
} catch (URISyntaxException e) {
// swallow anything that scares us
}
if (colonPos >= (workingName.length() - 1)) {
break;
}
workingName = workingName.substring(colonPos + 1, workingName.length());
}
return null;
}
}