Monday, December 15, 2008
House Music - Mediatomb and PS3, Again
First of all, I think one of the reason playlists didn't work for me was that, since I wasn't sure which would work better, I had both a pls and a m3u file with the same base name. I think this wasn't working well, not sure.... When I added a small m3u file, alone, it worked fine. Mediatomb created a new "playlist" section, and the PS3 could play it. I had not seen that before.
Given that, I went ahead and set Mediatomb to work scanning the full artist/album directories. This takes a long time (on the computer I used). Meanwhile, I was also able to add a few more, small, playlists.
I used fapg to create m3u files from the plain path/file lists out of my old database:
fapg -f m3u -s <> files.m3u
And they worked!
This step was important because what I had were just lists of files. I found that when the PS3 plays a playlist is displays information from the extended tags in the m3u file, not from the MP3 file tags (when playing by artist or album, the PS3 displays the MP3 tag data, however). I don't know if Mediatomb or the PS3 is deciding this, but I couldn't find any other jukebox software that would read in my list and save off a m3u file with extended information in the m3u file, from the MP3 tags. This in spite of the fact that all the programs I tried (three or four, there are a lot these days) would display the MP3 tag information on loading a simple file list... fapg saves the day. Converting a plain list to m3u with fapg causes it to read the MP3 tags and add the extended information to the resulting playlist file. Nice.
Here's a few observations and questions I still have about the Mediatomb/PS3 combination.
1. It seems to scan and run well up to some point. Then scanning and the response times at the web application and on the PS3 slow down dramatically, to the point of getting timeouts.
2. Large playlists, completely added on Mediatomb, do not appear all at once one the PS3. If I browse into a list, I get just 30 or 40 entries at first. More entries show up in bursts every few seconds after that. If I exit that list, and browse back, it picks up where it left off, but if I start playing the list, it only plays the tracks that had been listed so far. I hope it doesn't have to do that everytime the PS3 is restarted. Perhaps a more powerful server would resolve this. So for, it's been doing this while still scanning the full volume so that is a factor.
3. I added m3u files to Mediatomb with an add operation, not with a timed scan of the playlist directory. The later is what I want, but I'm not sure it works. I did try adding the directory with a scan, but the playlists were not created. Again, performance constraints may be a factor. Also, it takes a really long time to do this with larger lists so I'm not sure it's practical. How will I create, load and play dynamic lists? Say from a search result in other software?
4. The folks on the Mediatomb forum are responsive and helpful.
5. Mediatomb works great for photos.
One last thing. I was running Mediatomb two ways; one using init.d and one just at the command line, separate databases. While adding m3u files using the instance started at the command line, but not while doing the timed scan, I'd see this quite a bit on the console:
TagLib: MPEG::Header::parse() -- Invalid sample rate.
Many of my CDs are encoded with a variable bitrate, so that's probably the explanation. The error does not impact the ability to play the files at all, as far as I can tell.
When all's said and done, I can now listen to my playlists. But there's still a lot of missing functionality to be figured out - better searching and browsing, dynamicly created playlists, effective and scheduled rescanning, streams, and tracking what's played - all things my old system does. I am however, most sure that the Mediatomb/PS3 combination can be part of the solution.
Friday, December 12, 2008
Closing
http://www.crunchgear.com/2008/12/10/office-depot-closing-112-locations/
Office Depot is closing 9% of their North American stores. They're just the latest... I bet this is the beginning of the end of the ridiculous level of retail we've seen grow up over the past decade. Suburbs all the country are going to be littered with empty big-boxes that never should have been built in the first place.
Science
"Australian scientists studying illnesses of the brain believe that an inability to detect when someone is being sarcastic is a symptom of dementia."
Oh, ya sure, THAT's going to work, right, WHAT EVER..... They'll probably discover that irony actually causes mental illness, then where will be be?
"The AFP reporters apparently asked Hodges whether the test would still work on "people from countries not renowned for their appreciation of sarcasm or irony".
"Apparently he believed the test "could be modified".
PC Power Saving
http://hardware.slashdot.org/article.pl?sid=08%2F12%2F12%2F1347200
Monday, December 08, 2008
Wii Again?
"The Wii has emerged as one of the most sought-after gifts this holiday season, which has otherwise been lamented for a lack of new and hot products.
"On Black Friday, the day after Thanksgiving which marks the start of holiday shopping, the Wii was the most searched-for product on the Yahoo Shopping site. It was also the most popular product on eBay, with 3,171 of the consoles selling for an average price of $349 that day."
The Wii is what, three years old now? And Nintendo still has not figured out that maybe they should, oh I don't know, make more of them? I don't get it.Saturday, December 06, 2008
WholeFoods Shopping Humor
"Mom, just hang out around here. I'll go up there and check on my Thanksgiving order. Don't wander off."
" 'DON'T WANDER OFF.' What?! I'm not a child. I'll wander off if I want to."
"You'll get lost."
"No I won't."
.....
[cell ring]
"I'm lost."
Friday, December 05, 2008
Netbeans Project File Madness
[ERROR] file:/C:/MyProject/EJBs/src/conf/xml-resources/web-services/ThatEJB/wsdl/ThatEJB.wsdl is unreachable
Failed to parse the WSDL.
C:\MyProject\EJBs\nbproject\jaxws-build.xml:112: wsimport failed
This was in spite of the fact that I had removed the target using the IDE, as "by the book" as possible. It should have cleaned up its target references, but obviously did not, and it just wouldn't let go.
What I had to do was open up C:\MyProject\EJBs\nbproject\jaxws-build.xml and remove all the references to the ThatEJB target. I had to do this once a week or so in fact, as the jaxws-build.xml file sometimes gets rebuilt (I don't know what triggers this rebuild, it doesn't seem to happen all the time).
I finally found the true source of the reference. I had to edit this file:
C:\MyProject\EJBs\nbproject\jax-ws.xml
This file contains an XML block for, it seems, each EJB
Context Roots for EAR Files in Glassfish
It turns out that when using an EAR file, it doesn't matter what you put in the WSDL, in web.xml, or in sun-web.xml. The context root is controled instead by application.xml. In Netbeans, right-click on the enterprise application (EA) project, and select new and “Standard Deployment Descriptor”. This will create an application.xml file, in the project, based on your current web.xml. This application.xml file can be edited to set the context root.
When using a WAR file, the context root is controled by the usual deployment descriptor, as one might expect. An easy why to set a WAR file's context root, after deployment, is through the Glassfish server administration console. Go to "Applications" and "WebApplications" (on the left side of the administration page), click on the name of your application and you’ll be able to change the "Context Root" option. Then click “Save”.
Thursday, December 04, 2008
Fisker Karma
"Unlike the Volt, the Karma will have three operating modes. The default mode is "stealth," and it delivers optimal range and efficiency. Acceleration is governed to maximize battery life, and top speed is limited to 95 mph. Flip the switch to "sport" and you get unfettered access to all 408 ponies. "HEV" or charge-sustaining mode works a lot like a strong hybrid and provides deceleration engine shutdown, engine start-stop and charge sustaining."
Why not toss a few billion at companies like this instead of GM? Maybe we could finally get some good, American, electric cars built.
Monkeys
"If you don't believe that Monkeys have REAL Ultimate Power you better get a life right now or they will climb a tree near your home and stare at you with their sad monkey eyes until your brain hurts!!! It's an easy choice, if you ask me."
Monkeys... Who knew?
Wednesday, December 03, 2008
Yugo
"It's the end of the road for the Yugo.
"Production of the car has officially ended after almost 30 years."
..."The Yugo factory is to be transformed and in future it will be making a brand new model for Fiat, something that has already been nicknamed here as the "anti-credit crisis car". "
I saw a Yugo the other day... They have not been imported to the US for a long time, but I did not know they were still made.
Tuesday, December 02, 2008
Zima
"Many drinkers assume that Zima vanished shortly thereafter and has since existed solely as a punch line. But Zima actually survived for more than another decade, until MillerCoors pulled the plug on Oct. 10. Rarely has such a famously maligned product enjoyed such a lengthy run—a testament to its brewers' Madonna-like knack for reinvention. The Zima that died a quiet death last month bore little resemblance to the malternative that swept the nation during President Clinton's first term."
Zima... I had completely forgotten about this stuff. But I am sure that I still don't know anything about this picture of a dog.
Wednesday, November 26, 2008
Home Prices in Portland
http://www.bizjournals.com/portland/stories/2008/11/24/daily20.html
"The average price of a home sold in Portland dropped 8.6 percent by September 2008, compared with a year earlier, but that is still below the national average annual drop of 16.6 percent.
"According to the Standard & Poor’s/Case-Shiller Home Price Indices report released Tuesday, the price drop in the largest 20 U.S. cities between September 2007 and a year later was 17.4 percent. In the 10 largest cities, the drop was even bigger, at 18.6 percent."
Home prices in Portland continue to be somewhat of an exception to national trends. People just keep moving here...
Saturday, November 22, 2008
House Music - PS3 and Mediatomb
Friday, November 21, 2008
House Music - UPnP and The PS3
I've been experimenting with various ways of getting this to work with the broader goal of building a better digital music system for the house than I have had in the past. Results have been mixed.
First up, fuppes.
Fuppes is a DLNA server that runs on Linux. Downloading, building and installing fuppes is easy - the usual "./configure", "make" and "make install". Running fuppes at the command once, and then quitting right away is an easy way to setup it's basic files. With that done the file ~/.fuppes/fuppes.cfg will exist. Edit this XML file:
1) Add a shared directory with a few MP3 files in it.
2) Enable the PS3 support.
3) Edit the network section, if needed, the port, to set the Ethernet device to use and the IP addresses allowed to connect (this can all be left to default if you have a simple environment).
Now run fuppes again. Hit 'r' to rebuild the database. This can also be done from the web interface. The web interface is at http://yourhost:5080, unless you changed the port.
The DLNA server is now running. Over at the PS3, I found it discovered the server right away without my having to do anything. The new server was listed under media servers in the menus. I was also able to successful browse into the server from the PS3. The directory structure on my server was reflected in the PS3 menu. It played a file fine also.
Although encouraging this setup isn't all that great. I need playlist support, for one thing. The PS3 has playlist support of its own, but only for files on it's hard drive.
Fuppes can share m3u and pls files. I downloaded fapg to make some quick playlist files (I don't typically use playlist files, my old system requested one song at a time from a server I wrote myself). To build fapg, I also needed to download and install uriparser. Both where the typical "./configure", "make" and "make install". But to run fapg I also had to add /usr/local/lib to LB_LIBRARY_PATH:
typeset -x LD_LIBRARY_PATH=$LD_LIBRARY_PATH:/usr/local/lib
Remember that something like this has to go in the environment permanently somehow if you want to use the dependent tool all the time.
The good news is that fapg worked fine for making a quick version both types of playlists for all the files in my sample directory, recursively. Also, the fuppes status web page showed the playlist objects as correctly discovered.
The bad news is that the PS3 could not see the playlist files. In addition, the virtual folder system fuppes provides to access files by meta data simply doesn't work - the PS3 does not show the virtual folder (note that you have to copy the vfolder configuration file to ~/.fuppes manually (this is described in the fuppes wiki)). In fact, no meta data is available at the PS3 at all. It only browser by directory and file name.
At this time, further development of fuppes is apparently stalled.
Although it works as far as it goes, I have to try some other approaches.
Some other things I noticed... The PS3 menus can copy a directory of files off the fuppes server onto the local drive. Once there, a PS3 playlist could be created I suppose. Also, I noticed that although the PS3 will only start playing when I select a specific file, once that song has ended it does go on to play everything in that directory. I suppose I could, at the server, write scripts that made directories full of symbolic links to files, serve that directory and call it a playlist. Not a great solution...
Thursday, November 20, 2008
Customer Service
I found a support email address and sent an email. I got an automated response right away. But amazingly, with about two minutes, I got an email from a live person, saying that, yes, it wasn't working correctly and they had recieved payment and the order was on it's way. I went back to the order status page and sure enough, it showed the order as ready to go.
Nice to see customer service working out for a change, at one place at least...
Google Shuts Down Lively
"...despite all the virtual high fives and creative rooms everyone has enjoyed in the last four and a half months, we've decided to shut Lively down at the end of the year. It has been a tough decision, but we want to ensure that we prioritize our resources and focus more on our core search, ads and apps business. Lively.com will be discontinued at the end of December, and everyone who has worked on the project will then move on to other teams."
It continues to amaze me that business ignores the potential of this sort of virtual world, and the virtual worlds that exist, I find technologicly unimpressive. Google Lively is, was, particularly odd in that it was very primitive. It would have been a great thing, in 1998. But even stranger was the fact that Lively did nothing to add to Google's business; ads. Why was there no ad tie in with Lively? After deliberately avoiding making Lively a part of the business model, Google pulls the plug with the rather odd explanation that they wish to focus on "ads and apps."
As an interesting side-bar, for some reason I can't fathom, Google does not allow the use of either Google ads, nor Google Checkout with Google Sites. However I can (could) use Google Checkout on Google's other website builder, Google Page Creator. Google pulled the plug on Google Page Creator in August, replacing it with the appearently more limited Google Sites.
I sure hope there's a long term plan in all this someplace.
Wednesday, November 19, 2008
Nissan Brings Japanese Minicars to the U.S.
The Cube looks looks like a big marshmallow and brings Japan's love of small cars to America, where the 30-plus-mpg wonder rolls into showrooms next fall. As the name implies, the Cube is nothing more than a box on wheels that Nissan design chief Shiro Nakamura called "clever, witty and fun, with a unique blend of fashion and function."
Looks like an interesting car. I bet people will love these!
I don't see Nissan asking for $25 billion from the tax payer...
Monday, November 17, 2008
RCSWEII
Robot Chicken: Star Wars Episode II
Friday, November 14, 2008
iObama
"Today, President-elect Obama will record the weekly Democratic address not just on radio but also on video -- a first. The address, typically four minutes long, will be turned into a YouTube video and posted on Obama's transition site, Change.gov, once the radio address is made public on Saturday morning."
Worries
"Stocks adds to losses on economic worries"
Are financial news outlets just reusing the same headlines over and over and over and over and over for months at a time?
Thursday, November 13, 2008
Spider
Dear Jane,
Are you sure this drawing of a spider is the one I sent you? The spider has only seven legs and I do not feel I would have made such an elementary mistake when I drew it.
Regards, David
Hmmm, Bacon...
"Baconnaise"
...Making the world just a little more bacony.
Wednesday, November 12, 2008
Calling a BPEL WSDL From an Object Outside an SA
Next up, calling an existing OpenESB BPEL from a plain Java class that has been included in a Netbeans "Web Application" project and deployed to Glassfish in an "Enterprise Application" project. Firstly, the web service client can be added to the Web Application project by file only, not by URL. Adding the service client by URL fails to retrieve the imported XSDs for some reason. Adding the client by local file works fine and the required service and port classes are generated in the build directory, as normally. So far so good. I can add the call of the web service to any Java class as usual, by dragging the port reference into the editor. However, at runtime, the call fails:
javax.xml.ws.WebServiceException: Unsupported endpoint address:
at com.sun.xml.ws.api.pipe.TransportTubeFactory.create(TransportTubeFactory.java:144)
at com.sun.xml.ws.transport.DeferredTransportPipe.processRequest(DeferredTransportPipe.java:112)
at com.sun.xml.ws.api.pipe.Fiber.__doRun(Fiber.java:595)
...
A quick aside about adding the web service call... One should be able to do this by right-clicking in the editor and selecting "Web Service Client Resources" and "Call Web Service Operation". However this only lists WSDLs in EJB projects. You can cannot select a WSDL that leads to a BPEL in this way, for some reason.
Back to the unsupport endpoint problem, the issue has something to do with the port I'm guessing. I resolved this by editing the generated service class and replacing the file URI for the WSDL with a full URL, port and all. Now more needs to be done - the service class is in the build directory. It is recreated each time, and the edits are lost. So to do all this, you must create a new service class. This is easy.
1) Create a new Java package in the source folder.
2) Create a new (plain) Java class in there, with the same name as your service class.
3) Paste the source of the generated service class into the new class file.
The resulting new service class will not be rebiult with the project and can be edited, to change the WSDL location.
I had to make two other edits in this new class. The last "get port" method, at the bottom, in the generated class is somehow invalid in the newly created class. How it could be valid in one place and not the other is beyond me. It is not used, so I removed it. Secondly, the new service class will need imports for the other generated classes.
The Java class can then use this new service class, with the WSDL as an HTTP URL, to invoke the BPEL.
More Glassfish and Netbeans Fun
{Netbeans Projects}\Project_EA\dist\gfdeploy\Project_SERVLET_war\WEB-INF\lib
Shutting down Glassfish (completely, you may have to use O/S tools to do this) will allow the jar to be deleted and rebuilt.
This is a big problem because it seems that if you do an ordinary build, rather than a clean build, on the EA project the jar file may not be updated. In one case this lead me to get a very strange runtime error claiming that a general Java class used in the EJB project belonging to the given EA assembly, had no constructor of a given form when clearly it did.
com.iwsinc.cms.jobs.PSStatusCheckJob.<init>(I)V
java.lang.NoSuchMethodError: com.iwsinc.cms.jobs.PSStatusCheckJob.<init>(I)V
at com.iwsinc.cms.ejb.CMSTimerEJB.cmsWatchEJBOperation(CMSTimerEJB.java:47)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39)
...
What was happening was that the build process was quietly failing to update the EJB part of the EA project - the jar in other words, because it was held open and could be be deleted.This problem, I would guess, only happens on Windows.
Product Version: NetBeans IDE 6.1 (Build 200805300101)
Java: 1.6.0; Java HotSpot(TM) Client VM 1.6.0-b105
System: Windows XP version 5.1 running on x86; Cp1252; en_US (nb)
Userdir: E:\openesb\.netbeans\openesb
Tuesday, November 11, 2008
Creating a Timer EJB for OpenESB/Glassfish
Here's the tools:
Product Version: NetBeans IDE 6.1 (Build 200805300101)
Java: 1.6.0; Java HotSpot(TM) Client VM 1.6.0-b105
System: Windows XP version 5.1 running on x86; Cp1252; en_US (nb)
Userdir: E:\openesb\.netbeans\openesb
glassfish-v2-ur2-b04-patch-20080729
1) Create an EJB with a local interface in an EJB project. Call the EJB EJBTimer.2) Create a servlet in a web application project. Call it Watch.
3) create an enterprise application project and add both of the above
4) In the servlet code, right-click and use the wizard to add a reference to the EJB. It will generate this:
@EJB
private EJBTimerBean eJBTimerBean;
5) Add to the servlet code an init() lifecycle method, and include there a call to the EJB to start ticking (see the source below).6) Modify the servlet's deployment descriptor to create the servlet on deploy. In Glassfish, it's web.xml editor calls this "Startup Order". Fill in a one. Examine the "load-on-startup" tag in the web.xml, to see what this does.
7) Deploy the enterprise application. The servlet will be created and will start the timer.
Here is the timer EJB:
package com.me.ebj;
import java.util.Date;
import javax.annotation.Resource;
import javax.ejb.Timer;
import javax.ejb.SessionContext;
import javax.ejb.Stateless;
import javax.ejb.TimedObject;
import javax.ejb.TimerHandle;
import javax.ejb.TimerService;
@Stateless
public class EJBTimerBean implements EJBTimerLocal, TimedObject {
private SessionContext sc;
private TimerHandle timerHandle = null;
public void ejbTimeout(javax.ejb.Timer arg0) {
java.util.logging.Logger.getLogger(getClass().getName()).log(
java.util.logging.Level.INFO,
"CMS0000: EJBTimerBean: Tick");
}
public void ejbPostCreate() {
// This does not work:
Date now = new Date();
initializeTimer(now, 30000, "CMS_TIMER");
}
public void initializeTimer(Date firstDate, long timeout, String timerName) {
try {
TimerService ts = sc.getTimerService();
Timer timer = ts.createTimer(firstDate, timeout, timerName);
timerHandle = timer.getHandle();
java.util.logging.Logger.getLogger(getClass().getName()).log(
java.util.logging.Level.INFO,
"Timer started.");
} catch (Exception e) {
e.printStackTrace();
}
}
@Resource
public void setSessionContext(SessionContext ctx) {
sc = ctx;
}
}
Be sure the timer includes the setSessionContext() methods, with the @Resource annotation. Also, initializeTimer() must be declared in the bean's interface.Here is the servlet, whose only purpose in life is to start the timer (note that some basic servlet code has been left out, it's not important for the example at hand).
package com.me.servlets;It took me awhile to figure out that to do this (this way), the EJB and the servlet must end up in the same ear file, that the annotation in the servlet for the EJB needs to be the interface, not the bean itself, and that the EJB needs to get its SessionContext using a @Resource annotated method, just as shown above.
import com.iwsinc.cms.ebj.EJBTimerBean;
import com.iwsinc.cms.ebj.EJBTimerLocal;
import java.io.IOException;
import java.io.PrintWriter;
import java.util.Date;
import javax.ejb.EJB;
import javax.servlet.ServletConfig;
import javax.servlet.ServletContextEvent;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
public class Watch extends HttpServlet {
@EJB
private EJBTimerLocal eJBTimerBean;
protected void processRequest(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
}
public void init(ServletConfig config) throws ServletException {
super.init(config);
// Start the timer
// to-do: MAKE SURE only one timer is started!!
try {
eJBTimerBean.initializeTimer(new Date(), 30000, "CMS_TIMER");
}
catch (Exception e) {
java.util.logging.Logger.getLogger(getClass().getName()).log(
java.util.logging.Level.SEVERE,
"Exception", e);
e.printStackTrace();
}
}
public void contextInitialized(ServletContextEvent sce) {
}
public void contextDestroyed(ServletContextEvent sce) {
try {
// to-do: Stop the timer
} catch (Exception e) {
e.printStackTrace();
}
}
}
On deployment of the web application, an exception is thrown by the EJB. The cause is not known at this time. i found a handful of questions on the various forums asking about this. This exception does not seem to impact the timer in anyway, as far as I can tell.
java.lang.IllegalArgumentException: "timers"
com.sun.enterprise.management.support.OldTypesBase.oldObjectNameToJ2EEType(OldTypesBase.java:153)
com.sun.enterprise.management.support.oldconfig.OldProps.(OldProps.java:187)
com.sun.enterprise.management.support.LoaderOfOldMonitor.oldToNewObjectName(LoaderOfOldMonitor.java:265)
com.sun.enterprise.management.support.LoaderOfOld.syncWithOld(LoaderOfOld.java:415)
com.sun.enterprise.management.support.Loader._sync(Loader.java:548)
com.sun.enterprise.management.support.Loader.sync(Loader.java:522)
com.sun.enterprise.management.support.Loader.handleMBeanRegistered(Loader.java:209)
com.sun.enterprise.management.support.LoaderRegThread.processRegistration(LoaderRegThread.java:204)
com.sun.enterprise.management.support.LoaderRegThread.process(LoaderRegThread.java:253)
com.sun.enterprise.management.support.LoaderRegThread.run(LoaderRegThread.java:154)
Monday, November 10, 2008
Tuesday, November 04, 2008
Joe
"In the written report, Officer Bailey cited an ongoing investigation of a Toledo police clerk's alleged improper check of Mr. Wurzelbacher's records in a state database as the reason a warning was issued.
"This unit gave warning because of ongoing investigation of the Toledo Police Department involving Mr. Wurzelbacher and felt issuing a citation could have negative repercussions to the department and the city as a whole," the officer wrote."
FX
"...when CNN’s Wolf Blitzer talks with Candy Crowley from the Obama headquarters in Chicago on Tuesday evening, the correspondent could simultaneously appear in the New York studio—at least as a 3-D hologram. Really.
Although CNN's takes the prize for most extravagant election-night bells and whistles, each network will pack studios with their entire stable of anchors, pundits, analyst and correspondents, while bringing out every gizmo in their arsenals to break down the results—including such primary night favorites as CNN’s “Magic Wall and Fox’s “Bill-board.”
I'll have a map with some push pins! Nah, maybe I'll just skip it...
Thursday, October 30, 2008
Spread the Wealth
http://thinkprogress.org/2008/10/30/mccain-larry-king-wealth/
To me this appearance on Larry King sums up why McCain doesn't come off so well. He is really bad at spewing nonsense the way many others on the right do. McCain, of course, as any sane person does, actually has reasonable views on things (we might disagree with him, but he's (probably) not crazy (I think...)). When his natural inclination to be reasonable collides with hysterical political slogans and mud slinging, McCain does a poor job of explaining himself.
When McCain did his best, it was just him, on a bus, talking with people. He barely even had a staff at the time. The trouble is, as many pointed out back then, you can't run a national campaign that way.
Jeff Sexton