Wednesday, December 30, 2009

java.sql.SQLException: No suitable driver

Since I've been re-building my pc since a blue screen of death last week, I got to spend a frustrating morning trying to figure out why i was getting the following exception in my Java app when making a connection to a 2005 SQL Server using the Microsoft jdbc drive SQLJDBC4 sqljsbc4.jar


java.sql.SQLException: No suitable driver
at java.sql.DriverManager.getConnection(DriverManager.java:545)
at java.sql.DriverManager.getConnection(DriverManager.java:193)
at com.medrisk.collections.server.DataServiceImpl.MRConn(DataServiceImpl.java:61)
at com.medrisk.collections.server.DataServiceImpl.getCaseWorkerList(DataServiceImpl.java:149)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25)
at java.lang.reflect.Method.invoke(Method.java:585)

A lot of postings out there on news groups all reprimand the poor developer who hits this message that either is class path is wrong, the jar is not in the path, or the connection URL is wrong. Developers howl that this is not the case... any they may be right! The matter-of-fact responses out there insist that this error only comes from the class path or url, but a hint on the Microsoft doc hints at another problem - your version of java.

First Step for you is to read that doc!

Here is what i had to do to fix this problem on my pc running eclipse 3.5

Open a command prompt and type

 java -version.

This will be your first clue, but also try typing:

C:\windows\system32\java -version

Note if the responses are different. We're trying to get version 6 of java installed and everything pointing to it.

Here was my response before i fixed it


C:\Documents and Settings\bsautner>java -version
java version "1.5.0_15"
Java(TM) 2 Runtime Environment, Standard Edition (build 1.5.0_15-b04)
Java HotSpot(TM) Client VM (build 1.5.0_15-b04, mixed mode)


C:\Documents and Settings\bsautner>C:\windows\system32\java -version
java version "1.6.0_07"
Java(TM) SE Runtime Environment (build 1.6.0_07-b06)
Java HotSpot(TM) Client VM (build 10.0-b23, mixed mode, sharing)



turns out there was a java install on my pc when it was given back to me after the crash.

According to the Microsoft doc, you need to Java Version 6 or higher to use sqljdbc4.jar.

Download the latest version of the Java JDK to your pc and install it. As of this posting, here is where i got it:

http://java.sun.com/javase/downloads/index.jsp

Try running the java command above but from the install directory like this:

C:\Program Files\Java\jre6\bin\java -version

note if you got a higher version than before. You may now have the right version to use sqljdbc4.jar

Next, open eclipse and tell it to use that version of java by selecting the Window Menu - then Preferences. Expand Java And click on Installed JREs. Click Add and browse to C:\Program Files\Java\jre6.  Click finish and make sure jre6 is checked.

I'm hopeing that since you found this blog, you already have the jar files and connection classes, and i hope this solved your problem.

More samples and resources on using sqljdbc are available here

http://msdn.microsoft.com/en-us/library/aa342339.aspx

Wednesday, December 23, 2009

Authenticating an Android App to Google App Engine.

8/23/2010 update: This is a little out of date now. It works, but it makes you get a user id and password from your user. A lot of this code is now obsolete if you use account sync functions on android 2.2 and higher where you can get an auth token using the phone's credentials.

I posted an update to this article that has the code for using the phone's credentials here: http://javagwt.blogspot.com/2010/08/authenticating-android-to-app-engine.html



So you have a google app engine project running on myapp.appspot.com with all of your users data, and REST web services available so other apps can download xml from it over http - but now you want to download that XML to an app running on an android based phone AND provide google account credentials so users will download their data just like they're logged into your app.


I spent the last two days trying to put together samples of how to do this from all over the web. The challenge here is to have an Android Application call REST web services hosted on Google App Engine but to provide login account credentials so the App Engine code considers the request as logged in. If that makes sense to you, then you've come to the right place. I'm not sure if this is the most secure way to work with your app data, so please continue at your own risk. No warranty etc!

There are quite a few other people out there blogging about the same thing here, i found that each one of their posts were missing one critical piece of information. I'm posting here a class that is as straight forward as possible, you create an object with a username and password and from there you can load a URL from app engine with the authentication credentials you need.

The act of making http requests to an app engine site and providing credentials along with the request is to first get an auth token from google, which is a temporary string, then passing that token to the login page of your app, which in turn gives you an authentication cookie. Then add that cookie to the header of all of your future http requests.

Sounds simple enough....

I did learn that if you try to run this code using the android SDK in a plain java application with a reference to the SDK in your build path, you will get an error: Stub! whatever that means - what it means is that code using the android SDK needs to be ran in an android emulator.

I want to post the entire class for this in one piece, so please find it all at the bottom of this post. You can Create a new Android Project In eclipse, add a class called GoogleAuthentication and paste what i have below verbatim. Change the final string gaeAppBaseUrl at the top to point to your own app url yourapp.appspot.com

when you create an instance of the class, you pass the email and password to the constructor which gets the authkey and cookie. Sample of that is at the bottom of this post.

The GetLoggedIn() function is the only function here you don't need, but is an example of calling a REST web service URL that corosponds to a servelet running this code on the App Engine program Nimbits Data Historian.  When you load the url  http://app.nimbits.com/nimbits/Service/user  it returns false because you are not logged in when this code fires on the app engine:

public static User getUser() {
com.google.appengine.api.users.UserService u = UserServiceFactory.getUserService();
return u.getCurrentUser();
}

public static void checkLoggedIn() throws NotLoggedInException {
if (getUser() == null) {
throw new NotLoggedInException("False");
}
}


Our mission is to provide make the http request provide the header cookie so the above call returns true, and we have access to the serServiceFactory.getUserService();

If you look at the GetLoggedIn() function below, you'll see how i'm making the http request to that URL but adding the auth cookie needed in the header of the request that was created on contruction of the object.

Ok, here is the code:




import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.io.UnsupportedEncodingException;
import java.net.HttpURLConnection;
import java.net.MalformedURLException;
import java.net.ProtocolException;
import java.net.URL;
import java.net.URLConnection;
import java.net.URLEncoder;

import org.apache.http.HttpResponse;
import org.apache.http.client.ClientProtocolException;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.cookie.Cookie;
import org.apache.http.impl.client.DefaultHttpClient;

public class GoogleAuthentication {
private final String gaeAppBaseUrl = "http://nimbits1.appspot.com/";
private final String gaeAppLoginUrl = gaeAppBaseUrl + "_ah/login";
private final String googleLoginUrl = "https://www.google.com/accounts/ClientLogin";
private final String service = "ah";
private Cookie authCookie = null;


//sample function that makes an http request and downloads
//the result providing the auth cookie.
public String GetLoggedIn() throws IOException
{

String result;
String destUrl = "http://app.nimbits.com/nimbits/Service/user";
URL url = new URL(destUrl);
URLConnection conn = url.openConnection ();
conn.addRequestProperty("Cookie",authCookie.getName() + "=" + authCookie.getValue());
BufferedReader rd = new BufferedReader(new InputStreamReader(conn.getInputStream()));
StringBuffer sb = new StringBuffer();
String line;
while ((line = rd.readLine()) != null)
{
sb.append(line);
}
rd.close();
result = sb.toString();
return result;
}

//Constructor creates the cookie
public GoogleAuthentication(String googleAccount, String googlePassword)
{
if (authCookie == null)
{

try {
String authToken = GetToken(googleAccount,googlePassword);
authCookie = getAuthCookie(authToken);
} catch (ClientProtocolException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}

}
}


private String GetToken(String googleAccount, String googlePassword) throws MalformedURLException, ProtocolException, UnsupportedEncodingException, IOException
{
String token = null;
HttpURLConnection h = GetConnection(googleAccount,googlePassword);
token= extractAuthTokenFromResponse(h);
return token;
}


private Cookie getAuthCookie(String authToken) throws ClientProtocolException, IOException {
DefaultHttpClient httpClient = new DefaultHttpClient();
Cookie retObj = null;
String cookieUrl = gaeAppLoginUrl + "?continue="
+ URLEncoder.encode(gaeAppBaseUrl,"UTF-8") + "&auth=" + URLEncoder.encode
(authToken,"UTF-8");
HttpGet httpget = new HttpGet(cookieUrl);
HttpResponse response = httpClient.execute(httpget);
if (response.getStatusLine().getStatusCode() == HttpURLConnection.HTTP_OK ||
response.getStatusLine().getStatusCode() == HttpURLConnection.HTTP_NO_CONTENT) {

if (httpClient.getCookieStore().getCookies().size() > 0) {
retObj= httpClient.getCookieStore().getCookies().get(0);
}

}
return retObj;

}

private HttpURLConnection GetConnection(String username, String password)
throws MalformedURLException, IOException, ProtocolException,
UnsupportedEncodingException {
HttpURLConnection urlConnection;
URL url = new URL(googleLoginUrl);
urlConnection = (HttpURLConnection) url.openConnection();
urlConnection.setRequestMethod("POST");
urlConnection.setDoInput(true);
urlConnection.setDoOutput(true);
urlConnection.setUseCaches(false);
urlConnection.setRequestProperty("Content-Type",
"application/x-www-form-urlencoded");
StringBuilder content = new StringBuilder();
content.append("Email=").append(username);
content.append("&Passwd=").append(password);
content.append("&service=").append(service);

OutputStream outputStream = urlConnection.getOutputStream();
outputStream.write(content.toString().getBytes("UTF-8"));
outputStream.close();
return urlConnection;
}
private String extractAuthTokenFromResponse(HttpURLConnection urlConnection)
throws IOException {
int responseCode = urlConnection.getResponseCode();
System.out.println(responseCode);
StringBuffer resp = new StringBuffer();
if (responseCode == HttpURLConnection.HTTP_OK) {
InputStream inputStream = urlConnection.getInputStream();

BufferedReader rd = new BufferedReader(new InputStreamReader(inputStream));
String line;


while ((line = rd.readLine()) != null) {

if(line.startsWith("Auth="))
{
resp.append(line.substring(5));

}

}

rd.close();


}
return resp.toString();
}
}


here is is the code in the main android class that makes the request and displays the result on the screen



public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
String result = null;
TextView tv = new TextView(this);
GoogleAuthentication G;
try {
G = new GoogleAuthentication("email@gmail.com", "secretpassword");
result = G.GetLoggedIn();
} catch (IOException e) {
result = e.getMessage();
}

tv.setText(result);

setContentView(tv);
}


and voila

Thursday, December 3, 2009

Converting a Java Unix Epoch Time to a C# Date

I do a lot of coding that involves passing serialized objects between C# and Java based applications. When in Java I tend to work with dates as a long. In Java, a date can be represented as a long that is the number of milliseconds since the unix epoch which is defined as:

"Unix time, or POSIX time, is a system for describing points in time, defined as the number of seconds elapsed since midnight proleptic Coordinated Universal Time (UTC) of January 1, 1970, not counting leap seconds." (http://en.wikipedia.org/wiki/Unix_time)

If you have a date object in java, you just call the date.GetTime() function to return that value.

.Net does not work well with this and I just found myself coding happily along until i hit this and realized i had to write this handy little C# function i now share with the world:


  DateTime ConvertEpochToDate(long epochMS)
        {
            DateTime disco = new System.DateTime(1970, 01, 01, 00, 00, 00, 00);
            return disco.AddMilliseconds(Timestamp);
        }


You can see, we add the milliseconds i have to a new datetime object that has been initialized to the unix epoch