Do read the first part of this post. I will continue with the ANT build example.
This post is all about an ANT example. I will present an ANT build script that will compile a project, and will generate a JAR file.
(more…)
The Code Generator SPI is in that module, but so is the Highlighting SPI. There's several usecases that relate to this SPI, as you can read in that link. Yesterday I came across a very cool tutorial that describes one of them: Extending the C/C++ Editor in NetBeans IDE 6.0 to Provide Mark Occurrences Highlighting. That's an excellent tutorial by Sergey Grinev from the NetBeans C++ team in St. Petersburg. Let's get to know this SPI while doing something pretty useful: we'll create a mark occurrences plugin for HTML files on the NetBeans Platform (i.e., typically, in NetBeans IDE, but also applicable to any other HTML file open in an application on the NetBeans Platform):
Above, I selected one instance of "you" and then all matching instances were highlighted. Try it. Doesn't work, does it? That's because this functionality is not part of the IDE, but part of my plugin. If you're interested in this functionality as a user, get it here from the Plugin Portal:
http://plugins.netbeans.org/PluginPortal/faces/PluginDetailPage.jsp?pluginid=9441
To create the above, I simply copied Sergey's code, except that I read the content of HTML files rather than C++ (or C) files and then changed the layer.xml accordingly. My MarkOccurrencesHighlightsFactory is identical to Sergey's:
package org.netbeans.modules.markoccurrences; import javax.swing.text.Document; import org.netbeans.spi.editor.highlighting.HighlightsLayer; import org.netbeans.spi.editor.highlighting.HighlightsLayerFactory; import org.netbeans.spi.editor.highlighting.ZOrder; public class MarkOccurrencesHighlightsLayerFactory implements HighlightsLayerFactory { public static MarkOccurrencesHighlighter getMarkOccurrencesHighlighter(Document doc) { MarkOccurrencesHighlighter highlighter = (MarkOccurrencesHighlighter) doc.getProperty(MarkOccurrencesHighlighter.class); if (highlighter == null) { doc.putProperty(MarkOccurrencesHighlighter.class, highlighter = new MarkOccurrencesHighlighter(doc)); } return highlighter; } @Override public HighlightsLayer[] createLayers(Context context) { return new HighlightsLayer[]{ HighlightsLayer.create( MarkOccurrencesHighlighter.class.getName(), ZOrder.CARET_RACK.forPosition(2000), true, getMarkOccurrencesHighlighter(context.getDocument()).getHighlightsBag()) }; } }
Here's the layer.xml registration:
<folder name="Editors">
<folder name="text">
<folder name="html">
<file name="org-netbeans-modules-markoccurrences-MarkOccurrencesHighlightsLayerFactory.instance"/>
</folder>
</folder>
</folder>
And, finally, the CaretListener, which picks up whatever is selected and then finds matching occurrences:
package org.netbeans.modules.markoccurrences;
import java.awt.Color;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import javax.swing.JEditorPane;
import javax.swing.event.CaretEvent;
import javax.swing.event.CaretListener;
import javax.swing.text.AttributeSet;
import javax.swing.text.Document;
import javax.swing.text.StyleConstants;
import org.netbeans.api.editor.EditorRegistry;
import org.netbeans.api.editor.settings.AttributesUtilities;
import org.netbeans.spi.editor.highlighting.support.OffsetsBag;
import org.openide.util.RequestProcessor;
public class MarkOccurrencesHighlighter implements CaretListener {
private static final AttributeSet defaultColors =
AttributesUtilities.createImmutable(
StyleConstants.Background, new Color(236, 235, 163));
private final OffsetsBag bag;
private JEditorPane pane = new JEditorPane();
public MarkOccurrencesHighlighter(Document doc) {
bag = new OffsetsBag(doc);
pane = (JEditorPane) EditorRegistry.lastFocusedComponent();
pane.addCaretListener(this);
}
@Override
public void caretUpdate(CaretEvent e) {
bag.clear();
scheduleUpdate();
}
private RequestProcessor.Task task = null;
private final static int DELAY = 100;
public void scheduleUpdate() {
if (task == null) {
task = RequestProcessor.getDefault().create(new Runnable() {
@Override
public void run() {
String selection = pane.getSelectedText();
if (selection != null) {
Pattern p = Pattern.compile(selection);
Matcher m = p.matcher(pane.getText());
while (m.find() == true) {
int startOffset = m.start();
int endOffset = m.end();
bag.addHighlight(startOffset, endOffset, defaultColors);
}
}
}
}, true);
task.setPriority(Thread.MIN_PRIORITY);
}
task.cancel();
task.schedule(DELAY);
}
public OffsetsBag getHighlightsBag() {
return bag;
}
}
The only difference from Sergey's tutorial is the bit in bold above. Nothing magical, not using any NetBeans APIs, except that whenever a matching pattern is found, it is added to the "bag" (hello, lookup, I knew you well), and highlighted. The only difference between my implementation of "mark occurrences" and the typical implementation is that in my case one actually needs to select something, rather than simply putting the caret in the middle of some word. But that's fine, I think this does the job pretty well. Not only is this a nice introduction to this SPI, but now there is something I've been missing for a while—mark occurrences for HTML files. I've written to Vladimir Voskresensky to ask him what he did to make navigation between marked occurrences possible (as described here) because that would be very cool to add to this plugin. It works pretty well together with Ctrl-F (i.e., search), so that you can search for a word while using mark occurrences for another word (so, you have a double-edged search, as in Java files), as you can see here (orange is from Ctrl-F, while yellow is from mark occurrences):
I'm looking forward to exploring the Highlighting SPI in other contexts too, now that I have this example as an entry point.
|
From last week, mostly to keep a record of milestones in community projects: JAXB RI 2.1.7 Now Available. This bug-fixing release matches the version bundled in Metro 1.2. |
|
Anissa has created a screencast showing how easy it is to add a plugin to the
GlassFish v3
administration console.
Check it out
here Imagine the possibilities! You can add functionality as a developer for yourself, for your team or enterprise, or, if you are an ISV now your application can smoothly integrate within the GlassFish Server. BTW, did you know about our partner program? See announcement and page. |
PS. The screencast can also be downloaded for offline-viewing from the GF Wiki.
In this post, I will present how to read properties from XML files.
The Code Generation SPI consists of two interfaces. The CodeGenerator implementations registered for various mime types serve for creating code snippets and inserting them into documents on the Insert Code editor action invocation. The CodeGeneratorContextProvider implementations registered for the mime types could provide the respective CodeGenerators with an additional context information.
Fine. What does all that mean? Firstly, interestingly, it means that the CodeGenerator class (and its supporting classes) are now officially supported and are exposed to the NetBeans API Javadoc. So, read the description to get an overview of it all.
Secondly, guess what? You can create code generators for any MIME type you want. So, you could add one/more to your HTML files... such as here:
So, the above appears when I press Alt-Insert in an HTML source file. What happens when I select the code generator item is up to me (i.e., the implementor of the API). In the case of Java source files, you can make use of the rather cool (though very cryptic) Retouche APIs. Let this document be your friend. I have found it pretty tough going, but gradually things become clear. Below, step by step, is my first implementation of a code generator. In the process, you'll see the Retouche APIs in action.
import java.util.Collections;
import java.util.List;
import org.netbeans.spi.editor.codegen.CodeGenerator;
import org.openide.util.Lookup;
public class HelloGenerator implements CodeGenerator {
public static class Factory implements CodeGenerator.Factory {
@Override
public List create(Lookup context) {
return Collections.singletonList(new HelloGenerator());
}
}
@Override
public String getDisplayName() {
return "Hello world!";
}
@Override
public void invoke() {
}
}
<folder name="Editors">
<folder name="text">
<folder name="x-java">
<folder name="CodeGenerators">
<file name="org-netbeans-modules-my-demo-HelloGenerator$Factory.instance"/>
</folder>
</folder>
</folder>
</folder>
public class HelloGenerator implements CodeGenerator {
private JTextComponent textComp;
private HelloGenerator(JTextComponent textComp) {
this.textComp = textComp;
}
public static class Factory implements CodeGenerator.Factory {
public List create(Lookup context) {
Item textCompItem = context.lookupItem(new Template(JTextComponent.class, null, null));
JTextComponent textComp = textCompItem.getInstance();
return Collections.singletonList(new HelloGenerator(textComp));
}
}
@Override
public String getDisplayName() {
return "Hello world!";
}
@Override
public void invoke() {
}
}
I don't know whether the above is the optimal way of doing this, but at the end of the day we now have a JTextComponent. In the next part, I've simply taken code from the Java Developer Guide, referred to earlier, and implemented the invoke exactly as described there, so see that document in order to understand the code below:
@Override
public void invoke() {
try {
Document doc = textComp.getDocument();
JavaSource javaSource = JavaSource.forDocument(doc);
CancellableTask task = new CancellableTask() {
@Override
public void run(WorkingCopy workingCopy) throws IOException {
workingCopy.toPhase(Phase.RESOLVED);
CompilationUnitTree cut = workingCopy.getCompilationUnit();
TreeMaker make = workingCopy.getTreeMaker();
for (Tree typeDecl : cut.getTypeDecls()) {
if (Tree.Kind.CLASS == typeDecl.getKind()) {
ClassTree clazz = (ClassTree) typeDecl;
ModifiersTree methodModifiers = make.Modifiers(Collections.singleton(Modifier.PUBLIC), Collections.emptyList());
VariableTree parameter = make.Variable(make.Modifiers(Collections.singleton(Modifier.FINAL), Collections.emptyList()), "arg0", make.Identifier("Object"), null);
TypeElement element = workingCopy.getElements().getTypeElement("java.io.IOException");
ExpressionTree throwsClause = make.QualIdent(element);
MethodTree newMethod = make.Method(methodModifiers, "writeExternal", make.PrimitiveType(TypeKind.VOID), Collections.emptyList(), Collections.singletonList(parameter), Collections.singletonList(throwsClause), "{ throw new UnsupportedOperationException(\"Not supported yet.\") }", null);
ClassTree modifiedClazz = make.addClassMember(clazz, newMethod);
workingCopy.rewrite(clazz, modifiedClazz);
}
}
}
@Override
public void cancel() {}
};
ModificationResult result = javaSource.runModificationTask(task);
result.commit();
} catch (Exception ex) {
Exceptions.printStackTrace(ex);
}
}
import com.sun.source.tree.AnnotationTree; import com.sun.source.tree.ClassTree; import com.sun.source.tree.CompilationUnitTree; import com.sun.source.tree.ExpressionTree; import com.sun.source.tree.MethodTree; import com.sun.source.tree.ModifiersTree; import com.sun.source.tree.Tree; import com.sun.source.tree.TypeParameterTree; import com.sun.source.tree.VariableTree; import java.io.IOException; import java.util.Collections; import java.util.List; import javax.lang.model.element.Modifier; import javax.lang.model.element.TypeElement; import javax.lang.model.type.TypeKind; import javax.swing.text.Document; import javax.swing.text.JTextComponent; import org.netbeans.api.java.source.CancellableTask; import org.netbeans.api.java.source.JavaSource; import org.netbeans.api.java.source.JavaSource.Phase; import org.netbeans.api.java.source.ModificationResult; import org.netbeans.api.java.source.TreeMaker; import org.netbeans.api.java.source.WorkingCopy; import org.netbeans.spi.editor.codegen.CodeGenerator; import org.openide.util.Exceptions; import org.openide.util.Lookup; import org.openide.util.Lookup.Item; import org.openide.util.Lookup.Template;
package org.netbeans.modules.my.demo;
public class NewClass {
}
Press Alt-Insert anywhere in the source file, choose "Hello world!", and now you will see this instead of the code above:
package org.netbeans.modules.my.demo;
import java.io.IOException;
public class NewClass {
public void writeExternal(final Object arg0) throws IOException {
throw new UnsupportedOperationException("Not supported yet.");
}
}
Hurray. You have your first code generator. In an HTML source file, the above invoke method could be as simple as this, which would print <h2>hello</h2> at the caret:
@Override
public void invoke() {
try {
Caret caret = textComp.getCaret();
int dot = caret.getDot();
textComp.getDocument().insertString(dot, "<h2>hello</h2>", null);
} catch (BadLocationException ex) {
Exceptions.printStackTrace(ex);
}
}
The very cool thing about these code generators is that you can keep typing, i.e., your fingers don't leave the keyboard in order to go to a menu item via the mouse, etc. You're typing as normal, then you press Alt-Insert, you select something, and then you go on typing. Being able to make your own contributions to the list of Java code generators (plus, being able to make them for other MIME types) is a really powerful enhancement to NetBeans IDE.
Quick note: I just did a presentation with Arseniy Kuznetsov (NetBeans director) about NetBeans at FOSS camp. It was kind of cool to demo Matisse in front of people like Mark Shuttleworth :) I think the demos were well received and we also had a discussion about getting NetBeans into Main Ubuntu repository (it is now available in Ubuntu Universe). There seems to be a potential for NetBeans to become a really popular IDE among Linux developers - we support many languages they use. The main missing language seems to be Python - and as you probably know Sun hired two Python/Jython developers to work on that - so the future of Python support in NetBeans is very bright.
FOSS Camp was an unconference, actually we were the only presenters who were using a projector (sorry about that but it would be hard to draw features of the IDE on a flipchart :). I wonder how many of the Ubuntu guys noticed I delivered my presentation on OpenSolaris (Gnome looks almost identical on Linux and OpenSolaris). It was nice for a change to visit an unconference, it definitely has a more "human" atmosphere with more 2-way communication than we see at typical conferences. Maybe it's time for NetBeans to organize some unconference as well? Just an idea.
Collin wasted no time creating such an example. Unfortunately, resources being what they are, I wasn't able to get engineering cycles for reviewing the example. Because we generally don't include material that hasn't been reviewed by engineering, it was backburnered.
Meanwhile, Collin posted his example and some accompanying text, The Secret Life of a DefaultTreeModel on JavaLobby.
Collin has now posted his example on the JavaTutorials forum!
When you compile and run the example (it will run under JDK 5) it brings up a panel containing a tree with a root node. When you open the root node you'll see three child nodes. At this point you can right-click any of the nodes (control-click on a Mac) and you can add 50 children to the current selection, remove the current selection, or modify the text on the current selection.
Thanks for this very useful example, Collin!
-- Sharon Zakhour
I am glad to announce that Eclipse project creation review for the Maven Integration for Eclipse (m2eclipse) will be happening next Tuesday, May 20 at 11:00 AM (slides).
This post is in continuation of ‘Using property files - I’. Do read that before this one.
To make your Java applications flexible, you might want to give properties that keep on changing in properties file. A property file is simply a text file that has key-value pairs relation.
A while back I got something to read - a book about BIRT (written by John Ward and published by Packt Publishing).
John Graham already posted his review about the book here and here. He summarized the content very well so I don’t need to go into the detail about the chapters anymore. Thanks John!
The book is well structured and easy to follow. It starts with information about Eclipse and BIRT and explains what and how to download, install, etc. The style is clearly a tutorial style.
The examples used in this book are a mixture of small self-created examples and BIRT’s Classic Cars example. You will learn what pieces form a report, how to create reports and how to put the pieces together to get reports from a SQL database and also from XML or flat (CSV) files.
Although there is some comparison of BIRT to other reporting engines this book is better for you if you just want to learn and understand BIRT. It contains a chapter of how to deploy BIRT but the book is not of that much help if you are a developer that is looking at integrating BIRT into your own applications. The deployment just handles the BIRT Viewer that comes with BIRT.
The bottom line is: I can recommend this book if you want to learn, understand and use BIRT for creating reports. I also recommended it to my colleagues that wanted to learn BIRT.
Lukáš Krecan and I have been working on the Maven POM XML editor for m2eclipse. The editor is integrated with an open source Nexus Indexer search engine and provides completion on Maven artifacts and customizable context-sensitive templates. It will be included in the next 0.9.4 dev build, which will be available this week.
We also have customizable templates for parent, property, dependecy, exclusion, repository and plugin elements in the POM XML.![]()
This is just beginning and if you want to help us to improve it and implement additional features, here are a few ideas that you could work on:![]()
Grizzly has OSGi bundles available for a while, but we haven't made any noise about it yet to let the spotlight on GlassFish v3. Since OSGi is the current buzz, let's the monster enter the buzz circus...
Since release 1.7.0 (we are now shipping 1.7.3.3), Grizzly ships with jar file manifests that include appropriate OSGi bundle information. This means that you can import the Grizzly jars into an OSGi framework and use them, for example, to build a I/O application using the framework classes, to build a static Web Server or a Comet messages bus. Those jars are currently self-contained for the reason that you can use them for testing using a simple command like:
java -jar [bundle-name] -p [port] -a [war|jar|directory location] [Component_name]
As an example, you can:
% java -jar grizzly-messagesbus-webserver-1.7.3.3.jar -p 8080 -a jmaki-messagesbus-demo.warwhich start a simple Comet messages bus (I will soon blog about this new module). As usual, let us know what you think about those OSGi bundles, if they are ok or not, as none of us are OSGi expert. You can download those OSGi bundles from here...
We announced the results of our third fiscal quarter (Q3) on Thursday last week, and the results weren't what I, or any of us, wanted.
As you can read in the press release, we delivered $3.267 billion in revenue for Q3, roughly flat with a year ago. On that revenue, we delivered a GAAP loss of 4 cents (equal to the charge associated with the acquisition of MySQL, which closed within the quarter) - on that revenue, we generated around $320m in cash.
The low light of the quarter was revenue in the US - which declined year over year by nearly 10%, a big step down for a geography that typically contributes 40% of our total revenue. The highlight of the quarter was our India performance, up 30% year over year - and our chip multi-threading Niagara systems, which grew (billings) 110%.
We had growth in 12 of 16 geographies in which we sell, but a shortfall in the world's largest economy (and the largest in Sun's portfolio), is tough to make up elsewhere. So we showed no growth at the corporate level.
Despite a weak US economy, we still see growth and opportunity across the world. We are going to be making some changes as a result of the quarter, certainly, but not in our core vision or strategic direction - network infrastructure is being built out across the world, developers will continue to define its architecture and shape demand, and we will continue to position ourselves to drive and capture that market.
With that, I'll go through a few questions:
What happened in the US?
Late in the quarter, we saw a fairly aggressive slowdown - among smaller customers, and for larger systems (like enterprise servers and large tape libraries). As you recall, we left Q2 with a healthy backlog, lots of momentum, and feedback from customers that we were totally on the right track, so we were as surprised as anyone that deals started stalling in early March.
Why did big systems slow?
It's counterintuitive, but larger systems and purchase orders's are easier to slow down than smaller purchases. When you sell the systems and storage behind a big buildout, it's typically a long selling cycle, and a fairly long implementation process (systems aren't powered up the day they arrive). So holding off for a few weeks, either because you're spooked about the US mortgage crisis or because your CFO decided to put a pause on capital spending, is fairly straightforward.
And remember, our business is a portfolio - from high growth, low end blades and training services, to slower growth, high end enterprise systems an infrastructure software. There is no one system or product for all workloads, it's a portfolio.
So how are you going to adjust going forward?
We'll continue to diversify our business - geographically, and with the introduction of our Open Storage initiatives this past week and acquisitions like MySQL and Vaau, we'll continue moving into adjacent markets.
We also announced a restructuring plan, through which we'll be making targeted reductions in operating expenses. The net result will be the elimination of up to 2,500 jobs.
To be clear - we are taking assertive, and prudent steps to focus on growth opportunities, and to pull our cost structure in line with our business model. As we've done in years past, we're doing both - making choices to invest and disinvest.
Evolving companies are never done making choices.
Where did you grow in the quarter?
In 12 of the 16 geographies we serve - including India (up 30%), Brazil (up 20%), up in China, Russia, the Middle East, Canada, to name a few places. In general, the world continues to look to technology as a source of growth, automation and efficiency. Even our Wall Street business was up this past quarter.
On the product front, our focus on energy efficiency continues to pay off, with Niagara systems grew (billings) 110% year over year, and our newest (AMD, Intel and SPARC) blade systems growing at an even higher clip. The MySQL team delivered a great growth quarter, and Service revenues were up 3% (a major portion of which are software related, of course). Disk storage billings were up 6%.
Deferred product revenues were again up nicely, more than 25% - these deferred revenues tend to be for higher end systems and more complex configurations, with gross margins above the corporate average. Deferred Services were down, attributable to the ERP transition I mentioned earlier (we expect to recover that in Q4).
What didn't go well?
Enterprise systems, which were great growers in Q1 and Q2 (20% and 8% growth, respectively), were down in the quarter - and not specifically attributable to competition. We saw exceptional performance on our APL systems built with Fujitsu, and a strengthening partnership. Tape libraries were also down, although media sales were strong.
Given the size of both these line items, our higher volume lower end businesses were not yet at a sufficient scale to eclipse the slowdown on the lower volume, high end systems.
Why don't you just stop giving your software away?
Because we prioritize developer adoption. Let me give an example.
Last week, we saw a very high profile media company raise a considerable sum of money. They had not otherwise been on our radar. I sent a note to the head of our global sales team, given the fundraising had cited a growing infrastructure buildout, and asked if we'd made contact.
He said no, but we were immediately reaching out - and it turns out they're completely built around MySQL.
So before we arrived, before we were engaged, and before they began building out a large infrastructure, the MySQL team had scored a design win - ahead of the proprietary competition. What should we have charged them beforehand? No matter what it was, they wouldn't have used the product - startups and developers don't pay for software. But here's a diffrent question: what would we have paid them to select MySQL over the proprietary alternatives before embarking on a massive expansion?
Right question. We didn't pay them, the MySQL team earned their adoption.
Will they buy a license now? Maybe not, but we'll be well positioned if and when they, like Facebook or Nokia or the New York Times, do. And in the interim, it costs us nothing for the reference. I was with a bunch of startups at our StartupCamp this morning, and asked how many folks in the audience *didn't* use free software... no hands were raised. Why are we focused on startups? Because we're focused on all developers, in big companies and small.
How do you feel about the competition?
Just fine, we looked at the deals slowing in the US, competition wasn't our big issue - it's not that someone else was getting the purchase order, it's that no PO was being issued in the quarter. We're more exposed to the US markets, and potentially more exposed to discretionary purchases (although I don't really believe that servers are more discretionary than storage - they're converging). Avnet, one of our big distributors, had a similar experience in the US.
Why didn't you pre-announce the quarter?
We wanted to be sure, when we made our announcements, to have finalized our numbers and our plan to adjust our cost structure going forward. Given we're in the midst of an ERP transition, we were still finalizing work late into April. Secondarily, we needed to review our FY 2009 restructuring plan with the board before going public. We announced as soon as we'd met, reviewed and approved the plan.
How did you lose money compared to a year ago profit?
Well, although we generated a lot of cash in the quarter (more than $320m from operating activities), we also incurred a number of charges which reduced our net income. These included non-cash items related to stock-based compensation and amortization of acquisition-related intangible assets as well as other acquisition-related charges - all of which added up to 20 cents worth of charges.
Are you repurchasing your own shares?
We don't comment on buyback plans, but we'll report any potential purchases at the end of the quarter.
When will the US recover? Will the malaise spread overseas?
We build network innovation at Sun, we don't predict the global economy.
And with that, you've hopefully got a clearer sense of what we saw, and what we see. So I'll end on a particuarly vexing question,
"Why does Sun's CEO waste time writing that blog?"
Because I believe in providing clarity surrounding our strategy and operations - not just once a year in the Annual Report. I believe clarity behind our direction is useful for our shareholders, customers, partners and employees.
In good times, and in challenging ones.
________________
Safe Harbor Statement
Jonathan's blog contains forward-looking statements regarding the future results and performance of Sun including statements with respect to the effects of our restructuring plan, and expectations for deferred revenue. These forward-looking statements involve risks and uncertainties and actual results could differ materially from those predicted in any such forward-looking statements. Factors that could cause actual results to differ materially from those contained in such forward-looking statements include: risks associated with developing, designing, manufacturing and distributing new products; lack of success in technological advancements; pricing pressures; lack of customer acceptance of new products; the possibility of errors or defects in new products; competition; adverse business conditions; failure to retain key employees; the cancellation or delay of projects; our reliance on single-source suppliers; risks associated with our ability to purchase a sufficient amount of components to meet demand; inventory risks; and delays in product development or customer acceptance and implementation of new products and technologies. Please also refer to Sun's periodic reports that are filed from time to time with the Securities and Exchange Commission, including its Annual Report on Form 10-K for the fiscal year ended June 30, 2007 and its Quarterly Reports on Form 10-Q for the fiscal quarters ended September 30, 2007 and December 30, 2007. Sun assumes no obligation to, and does not currently intend to, update these forward-looking statements.
Eclipse provides a window called ‘Open Resource’ window to locate the required files in no time.
I will continue exploring RecordStore with examples in this post.
(more…)
Our ICEFaces/Comet/Grizzly Framework sessions at JavaOne were well attended and we have decided to give a couple of replay across Canada. Sessions on AjaxPush, GlassFish/Grizzly Framework and ICEFaces will be available May 27 (Toronto), May 29 (Montreal) and June 3 (Vancouver). Click here to register. I'm tempted to do the Montreal one in French ;-) Just need to convince Ted :-)
Chtěl bych vás všechny pozvat na následující akci, kterou organizujeme spolu s ČVUT v Praze. Vstup je volný pro všechny a bez registrace.
I would like to invite all of you for the following event, that we are organizing with CTU in Prague. The entrance is free for everyone, no registration is neccessary.
Sun Microsystems a České vysoké učení technické v Praze pořádají setkání české vývojářské komunity s pracovníky Sunovské divize MySQL. Setkání proběhne dne 19. května od 18:00 v místnosti 309 na ČVUT v Dejvicích (adresa: Technická 2, Praha 6 - Dejvice, viz mapa). Délka setkání bude cca. dvě a půl hodiny, vstup je volný a není třeba se registrovat. Prezentace proběhnou v anglickém jazyce. Budou přítomni vývojáři produktu MySQL i management, takže budete mít možnost se zeptat na celou škálu dotazů včetně otázek ohledně budoucnosti MySQL či položit dotazy velmi technického rázu.
Abstrakty prezentací:
1. MySQL - the Community, the Product, the Company
An overview of all things MySQL, from a combined technical and business
perspective. A short history of MySQL, the company, the product. What
the MySQL Community is, and how MySQL works with it. How MySQL is being
integrated into Sun.
Speaker bio: Kaj Arnö, VP MySQL, Sun Microsystems
Kaj joined MySQL in 2001, after 14 years as an entrepreneur. He split
his company into two, selling the half focusing on MySQL Training to
MySQL AB and launching MySQL's training department as VP Training. Since
then, Kaj has been VP Professional Services, VP Services, and VP
Engineering at MySQL, before assuming his last pure-MySQL role of VP
Community Relations in 2005. With MySQL AB being acquired by Sun, Kaj
continues to lead the MySQL community efforts, but devotes most of his
time to his role as MySQL's Ambassador to Sun. This involves meeting
with Sun teams, customers and communities across organisational and
geographical boundaries.
2. MySQL Workbench - Native Cross Platform Development
MySQL Workbench is a cross-platform, visual database design tool
developed by Sun. It is now released on Windows and will be available as
a native GUI tool on Linux, Solaris and OS X. After a short overview the
tool will be demonstrated in action - followed by a discussion of its
architecture and scripting engine.
Speaker bio: Michael G. Zinner, Team Lead, Developer Tools, Database
Group, Sun Microsystems
Michael G. Zinner joined MySQL AB in October 2003 as the lead of the
MySQL GUI team. Mike is responsible for the design and the development
of the graphical MySQL tools including the MySQL Administrator and MySQL
Workbench. Prior to joining MySQL AB, Mike was developing database
related GUI tools, including a highly acknowledged visual database
designer for MySQL, DBDesigner 4.
Let continue exploring RecordStore.
(more…)
I will use several examples to show how to use RecordStrore in J2ME applications.
(more…)
JavaOne wrapped up on Friday. We hosted individuals from across the globe, and from every industry: consumer electronics and gaming, to enterprise IT, space exploration, factory automation, the automotive industry, academia - like the network itself, Java delivers something for nearly everyone, everywhere.
This year's biggest announcements centered around Java's role in the future of rich internet applications (or RIA's). What's a rich internet application? It depends on your perspective - from mine, it's any network connected application that persists in front of a user, typically outside a browser, that can operate when disconnected from the network.
On the one hand, I'd claim Java's always been a RIA platform - before the world really wanted one. Early Java applets delivered interactivity, but at the expense of development complexity and, in the early days, performance - when a browser, and more recently Javascript, would suffice.
But browser based applications are hitting complexity and performance limits, and content owners are striving for higher levels of engagement (via high definition video, or advanced interactivity). Developers are demanding something new - the browser's a wonderfully accessible programming model, but it's a weak deployment model for rich/disconnected applications.
An unspoken driver of RIA is also business model evolution - many companies behind rich applications are seeking independence from browsers and search engines, whose default settings and corporate parents present a competitive threat. There's a growing appetite for locally installed applications that build rich, direct and permanent engagement with consumers. No one wants to pay a toll to meet their own customers.
With that in mind, as we looked to reinvent the Java platform, we heard a consistent set of requirements. And not just from coders, but from sports francishes seeking to directly engage their fans, media companies wanting to bypass browser defaults, to artists and businesses and device manufacturers - everyone's looking to uniquely engage consumers via the network. These audiences have nearly identitical requirements for a RIA platform - they want technology that:
At JavaOne last week, we addressed every one of those issues - here's how:
First, RIA developers want to reach every consumer on earth, and on every device.
Why? Because the market is in front of consumers - no matter what screen they may be using. Desktop, mobile phone, personal navigation, digital book - you name it. The market's in front of all the screens in your life, not just a PC.
That said, on PC's alone, Java's popularity has grown in the last few years, as measured by runtime downloads - we routinely download 40 to 50 million new Java runtimes a month, and update more than a billion every year.
The adoption of the Java platform exceeds the adoption of Microsoft's Windows itself - Sun's Java runtime environment (JRE) is preloaded on nearly every Windows machine (from HP, Dell, Lenovo, etc.), but also runs on Apple's Macintosh, Ubuntu, Fedora, SuSe, Solaris and OpenSolaris desktops. In addition, a JRE is present on billions - yes, billions - of wireless and mobile devices, from automobile dashboards and navigation devices, to Amazon's Kindle (did you know Amazon's Kindle is a Java platform?).
Which is to say, the Java platform reaches more people than any other software technology the world has ever seen.
Second, RIA developers want performance, functionality AND simplicity.
Why? Because content owners and application developers want to engage consumers - and want to engage artists and creative professionals in the workflow.
Java's history with simplicity isn't perfect - which is why our teams have rewritten the applet model, and focused so intently on making the new consumer Java runtime environment (download a beta version here) exceptionally fast to load within a web page, exceptionally performant for complex interactivity, and trivially accessible to consumers. We've also simplified Java with a scripting language, JavaFX script, that enables creative professionals to engage with coders to create immersive experiences, while embracing the creative tool chain (from interaction design to pixel manipulation) used by the worlds designers and digital artists.
And I'm really pleased we've solved the desktop installation problem, by making JavaFX applets separable from a web page with a simple drag and drop (click the image above to watch this demonstrated). Developers can now bypass the browser to trivially install apps on desktops - once the applet's dropped on the desktop, content owners have a direct relationship with their consumers.
You might have also seen that we're adding full high quality audio and video codecs to Java on every platform on which it runs - resolving another gap for RIA developers, support for time-based media (click here for a demo of high performance video).
Third, enterprises want to reuse their existing Java skills and assets in moving to RIA.
Nearly every enterprise employs programmers with Java skills - it's still the number one internet language taught across the world, and found pervasively in global business infrastructure. As businesses move to engage their customers via RIA platforms, reusing existing skills, and connecting RIA's to existing systems, gives the Java community a unique ability to build from what exists - rather than attempt to replace it.

This familiarity also allows businesses and developer teams to focus on engaging with consumers - rather than irritating IT with new infrastructure requirements (JavaFX developers simply link to existing enterprise infrastructure, vs. requiring new systems for RIA apps).
Fourth, RIA developers want free and open platforms.
Why free? Because developers don't want to encumber their applications with royalty bearing dependencies, or use technologies that predefine where consumers might appear. You don't build developer communities around closed source, you build user communities -
and this is an instance where developer selection and adoption will define the broadest RIA marketplace. JavaFX will, like all of Sun's software platforms, be made freely available as open source, and it'll be released via the GPL (v2) license.
And lest you think free and open software is the province of those with goatees and tattoos... we're seeing a rising tide of developing nations mandating free and open software in government and academic procurement. Why? To protect choice, and build indigenous opportunity - there's no reason to build dependencies upon proprietary software if you can avoid it.
Lastly, lets face it, the real value in Web 2.0 is the data - not the app. And that data is YOURS.
If you've been watching the social media space as carefully as we have, you understand the value of instrumentation and intentionality in building a business on the web. Knowing what users are doing with your product, whether it's a fantasy cricket league or a consumer banking application, enables more innovative business models, the delivery of higher value services, placement of more valuable ads - data allows for better decisions, and better value creation (and bluntly put, higher CPA).
But most rich internet applications are built, then deployed - into a fog. Developers who leave the confines of the browser either lose access to information about what their users are doing, or have to rely upon a technology provider that's inserting itself into their data stream. And some of those technology providers compete with content developers.
With a project code named Project Insight, we'll be instrumenting the Java platform to enable developers to harvest the data stream generated by their RIA content. JavaFX developers can focus on their business models - rather than enhancing someone else's.
_______________________
With all that said, what's the success of JavaFX worth to Sun?
By definition, it's worth more to Sun than the adoption of someone else's platform (known as "positive option value") - and the proprietary infrastructure used to serve it (don't forget, RIA's have rich internet back-ends (RIBs?). And in the RIA world, all the options are going to be priced at free, anyways - this isn't a contest to be won on price.
From where I sit, the platform likely to win will be the one that sets developers free - to pursue markets, opportunities and customer experiences as they define them, not as vendors define them. Now, setting developers free - that's where we can excel. It's in the DNA of everything we do.
For developers, learn more at JavaFX.com. And be sure to check out NetBeans - like Java itself, it's starting to rock the free world...
Over on the javatutorials portal Collin Fagan has posted an example that shows how to do just that. Check out Row Labels/Frozen Columns. (You'll have to scroll down through several posts to see it.)
Thanks Collin!
-- Sharon Zakhour
Last week at Community One day, the jMaki session included a couple of speakers from the community. It is great to see jMaki used in the real world.
We had four speakers at our session and I'll admit it was a lot given we only had an hour. But I was excited to include both Jennifer Myers and Daniel Ziaoure since they are both using jMaki in their projects.
The session was divided as follows. I gave a high level overview of jMaki and talked about the framework, widgets and pub/sub bus. It was pretty quick since I wanted to give others time but I was able to squeeze in a short demo.
Jennifer who also works for Sun talked about project Miso and the interface that they built using jMaki. Project Miso, provides deep, fast and broad search and indexing services and will initially be integrated into the Communication Suite. Currently the project is focussed on email, calendar and Instant Messaging products from Sun. Jennifer used the Yahoo table to display the list of items found in the search and the dcontainer to display the image that was found. Jennifer mentioned that she had no prior Ajax experience prior to starting on the project and found jMaki a great tool for quickly developing the user interface.
Daniel Z, an employee of TravleMuse Inc., covered the architecture of his project and showed how jMaki is used in their site. TravelMuse helps travelers save time and make better decisions at every step of the travel planning process by providing the Web’s most user-friendly travel planning experience. Daniel explained how the jMaki framework is used for developing widgets and for widget to widget communication. The application is JSF based. The site is live now with many enhancements coming in the next month at http://travelmuse.com.
And Greg closed with some of the new features like the performance enhancements and the jMaki webtop that is now available in jMaki 1.8.
To prepare for our JavaOne session, GWT and jMaki: Expanding the GWT Universe, I decided I should add a jMaki Yahoo widget to a GWT application. Here is what I did.
I wanted to start with an existing application and I chose the Java PetStore demo application which Greg had rewritten using jMaki widgets and GWT. The code for that application is in the jmaki-store project along with a README.txt containing all the build and install instructions. First I checked out the jmaki-store workspace, built and deployed the application to make sure I had a working copy of the app. Follow the instructions in README.txt file to build and deploy but basically you need to go to the jmaki-store/code/gwt/webapp dir and type ant. There also is a target to deploy the application or you can copy the war file to the auto deploy directory. In my case I chose to deploy to GlassFish V3 and deployment was extremely fast.
I decided to add the Calendar Yahoo widget to the application because I thought it would be pretty straight forward. The following are steps needed to add the widget to the application. First, I had to add an additional class (Calendar, java) in jmaki-store/code/gwt/widgets/sc/java/jmaki/client. Calendar.java contains the following:
package jmaki.client;
import jmaki.client.JMakiWidget;
/**
* Calendar Widget.
*/
public class Calendar extends JMakiWidget {
public Calendar(int width, int height) {
super(width,height);
}
public String getWidgetName(){
return "yahoo.calendar";
}
}
This class extends JMakiWidget which is a wrapper for the jMaki widgets. The Calendar widget has 2 methods, a constructor and the getWidgetName method which returns the name of the widget. Following the jMaki convention the name of the widget is the toolkit name dot widget name.
Next I updated the Widgets.gwt.xml file to list all the dependencies for this widget. It turns out that the list is already in the widgets.json file as that information is needed by the jMaki plugins so I started there. I had to update the paths and reorder the list because here order matters. The Yahoo toolkit CSS files need to appear before jMaki CSS files then the Yahoo toolkit library dependencies and then the jmaki component.js file. jMaki framework will take care of this automatically but in GWT land the list needs to be specified in order. The Widgets.gwt.xml file looks like:
<?xml version="1.0" encoding="UTF-8"?> <module> <inherits name="com.google.gwt.core.Core"/> <source path="client"/> <public path="public"/> <script src="resources/jmaki.js"/> <stylesheet src="resources/jmaki/ibrowser/component.css"/> <script src="resources/jmaki/ibrowser/component.js"/> <stylesheet src="resources/jmaki/resources/styles/themes/charcol/theme.css"/> <stylesheet src="resources/jmaki/accordionMenu/component.css"/> <script src="resources/jmaki/accordionMenu/component.js"/> <stylesheet src="resources/jmaki/cart/component.css"/> <script src="resources/jmaki/cart/component.js"/> <stylesheet src="resources/jmaki/feedreader/component.css"/> <script src="resources/jmaki/feedreader/component.js"/> <stylesheet src="resources/yahoo/resources/libs/yahoo/v2.5.1/calendar/assets/skins/sam/calendar.css"/> <stylesheet src="resources/yahoo/calendar/component.css"/> <script src="resources/yahoo/resources/libs/yahoo/v2.5.1/yahoo-dom-event/yahoo-dom-event.js"/> <script src="resources/yahoo/resources/libs/yahoo/v2.5.1/element/element-beta-min.js"/> <script src="resources/yahoo/resources/libs/yahoo/v2.5.1/container/container_core-min.js"/> <script src="resources/yahoo/resources/libs/yahoo/v2.5.1/menu/menu-min.js"/> <script src="resources/yahoo/resources/libs/yahoo/v2.5.1/button/button-min.js"/> <script src="resources/yahoo/resources/libs/yahoo/v2.5.1/datasource/datasource-beta-min.js"/> <script src="resources/yahoo/resources/libs/yahoo/v2.5.1/calendar/calendar-min.js"/> <stylesheet src="resources/yahoo/resources/libs/yahoo/v2.5.1/menu/assets/skins/sam/menu.css"/> <stylesheet src="resources/yahoo/resources/libs/yahoo/v2.5.1/button/assets/skins/sam/button.css"/> <script src="resources/yahoo/calendar/component.js"/> </module>
So far I have listed the resources needed for the widget and now I need to copy those files into the web app. All the resources are in the jMaki bundle so I just need to get what is on the list and add it to my application. Here I cheated a little. I created a project using NetBeans and put a single widget (Yahoo calendar) in the page. Doing so copied in the appropriate resources and so I had the directory I needed to copy for my GWT app. The resources/yahoo/calendar/* directory under the new project was copied to the jmaki-store/code/shared/widgets/resource/yahoo directory in the jmaki-store project.
The last thing I need to do is to add the new widget to the Main window of my app. Edit jmaki-store/code/gwt/webapp/src/java/jmaki/store/client/MainEntryPoint.java to look as follows:
package jmaki.store.client;
import com.google.gwt.core.client.EntryPoint;
import com.google.gwt.user.client.ui.RootPanel;
import com.google.gwt.user.client.ui.DockPanel;
import com.google.gwt.user.client.ui.Widget;
import jmaki.client.IBrowse;
import jmaki.client.AccordionMenu;
import jmaki.client.Cart;
import jmaki.client.Calendar;
public class MainEntryPoint implements EntryPoint {
/** Creates a new instance of MainEntryPoint */
public MainEntryPoint() {
}
/**
* The entry point method, called automatically by loading a module
* that declares an implementing class as an entry-point
*/
public void onModuleLoad() {
DockPanel dp = new DockPanel();
AccordionMenu am = new AccordionMenu("menu.json", 200,400);
dp.add(am, DockPanel.WEST);
IBrowse ib = new IBrowse(400,400);
RootPanel.get().add(ib);
dp.add(ib, DockPanel.CENTER);
Cart cart = new Cart(250, 400);
dp.add(cart, DockPanel.EAST);
Calendar cal = new Calendar(150, 150);
dp.add(cal, DockPanel.EAST);
RootPanel.get().add(dp);
}
}
Now when you build and deploy the app again you will see the calendar widget in the page. Note that you do not need to change the build files.
It really is pretty easy once you figure out the order that the JavaScript dependencies should be in. Using jMaki wrapped widgets means that 3rd party components can easily be added to GWT.
My keynote is easy. Everybody else doing talks at JavaOne has to figure out what to say. I poke around the community and grab stuff. There's so much cool stuff being done that the hardest part of putting the toy show together is picking. I just have to stand back in awe and ask a few inane questions.
Controlling the most complex instrument ever made by mankind.... (the Large Hadron Collider) Surfing a constellation of satellites around mars and mining their data... A pen as a computing platform... The realtime stuff becoming mainstream.... Instrumenting the world... Another generation of smart cards... And smart cars... Massive graphical acceleration on a cell phone... Killer massively multiplayer games... Great web infrastructure tools for creating and introspecting... "the network is the computer" Hah! => "the network is the world"
But the best part was helping to give John Gage his well deserved lifetime achievement award.
Now it's time for a beach and a beer.
Here are the few highlights from the talks that I attended today:
TS-5428 Java Technology Meets the Real World: Intelligence Everywhere.
This talk is about pervasive computing (a.k.a ubiquitous computing) with products from Sentilla. There was an interesting demo about humidity sensor detecting changes and sending a message to a host. The "motes" run CLDC 1.1 VM (+ proprietary profile for motes). These motes have ports for sensors and actuators and some built-in sensor. There were many interesting suggestions for embedded programming for such small devices (don't allocate in inner loops and there by leading to to GC kick-in, avoid too many static fields, avoid threads whenever possible and so on).
TS-7575 Using Java Technology-Based Class Loaders to design and implementing a Java platform, Micro Edition
The basic idea is to run JavaME applications (developed for different configurations/profiles/subsets of optional packages) on top of JavaSE. The extended JavaSE classes and packages not available in specific profile or optional package set [implemented by a specific phone] should not be made available to JavaME apps targeted. i.e., only the classes available to a specific phone model should be available. If the JavaME app tries to access any other class, it should receive ClassNotFoundException. The speakers explained how to achieve such "containers" by class loader based isolation. The problem is that they seem to solve only the class access. What about extended methods and fields? For example, platform core classes on JavaSE have superset of methods [more methods on the same class available on JavaME - eg. java.util.Hashtable has more methods on JavaSE). The application classes have to bytecode analyzed and instrumented to take care of field/method accces. It seems that their current product that does not address this yet.
PAN-5542 Developing Semantic Web Applications on the Java Platform.
The discussion started with some nice demos. There was a demo with AllegroGraph RDF store, Twine, a demo with using GRDDL and getting RDF triples by a proxy server. i.e., a proxy serves does the GRDDL transformations to get RDF triples from sites [which could be stored/analyzed with RDF stores subsequently] and a demo with FOAF files. Interesting take aways from the discussion include:
See you there!
We announced the first commercial release of OpenSolaris - targeting high speed developers and development teams (not consumers...). OpenSolaris focuses on developers wanting to be freed from proprietary software models, who see innovation and automation in operating systems as a source of competitive advantage.
If Solaris 10, OpenSolaris's older brother, is for IT departments prioritizing carrier grade stability over rapid innovation, OpenSolaris targets the exact opposite - developers, from high performance computing to social networking, that prioritize a constantly refreshing repository filled with community innovations (and ZFS-based automated rollback) over an unchanging qualification target. Go to OpenSolaris.com to download a free copy, or click on the OpenSolaris logo to have a bootable CD delivered to you (free of charge). Or if you want a simpler way of trying it out... just go to Amazon!
We also announced a partnership with Amazon, through which we've made OpenSolaris, alongside MySQL and Glassfish, available with commercial support on Amazon's elastic computing cloud. From where I sit, this is a profound change in the industry - the world's most popular database is now available, and commercially supported, as a cloud service. As is the fastest growing Java container, and a redefined OpenSolaris for the modern world.
The traditional software industry, first revolutionized by open source, next by software as a service, is now embarking on a third revolutionary change... infrastructure as a service.
Sure feels like the clouds are parting.
(And again, if you'd like a free copy of OpenSolaris sent to you on a bootable, "live" CD, just click on the OpenSolaris logo above.)
Today Bill, Chihiro, Jaya and I talked on Blu-ray. The talk was centered around the open source project @ http://hdcookbook.dev.java.net - a library and a set of tools to build Blu-ray discs. If you haven't checked out code/docs, you may want to checkout and play with the code. All you need is a laptop with blu-ray drive and a BD-RE disc. Optionally, for added fun you may want to have a hardware bluray player such as PS3 -- so that you can see the output on your TV rather than on a laptop. Other than the session, we also had a very informal BOF on blu-ray, OCAP etc. during the evening. It is good to meet experts in respective technologies in one place!
Other than the the blu-ray stuff, I did attend other talks/BOF. Just after Blu-ray session, I attended "TS-6000 Improving Application Performance with Monitoring and Profiling Tools" talk. This talk was about OS specific tools, JDK tools and third-party tools for profiling and monitoring. Gregg Sporar and Jaroslav Bachorik (NetBeans Profiler team) presented very well. There were many interesting questions/discussions as well. If you haven't done so already, you may want to download VisualVM. If you want bit more fun doing monitoring/profiling, you may want to check out the sources from http://visualvm.dev.java.net and build it yourself. You can build BTrace VisualVM plugin using the command:
c:\visualvm\plugins>ant build
assuming you have checked out VisualVM sources under "c:\visualvm". If you have already checked out BTrace sources under some other directory, say "c:\btrace", you can use
c:\visualvm\plugins>ant -Dbtrace.home=c:\btrace build
To run VisualVM with all the plugins that you built, you can use the following command:
c:\visualvm\plugins>ant -Dbtrace.home=c:\btrace run
Please let us know what features you'd like to see with BTrace and/or BTrace VisualVM plugin.
I attended and liked the "Class Loader Rearchitected (BOF-6180)" BOF. If you have ever written class loaders, chances are that you have faced mysterious deadlocks or ClassCastException that said "ClassCastException: Foo cannot be cast to Foo" or having to decide between overriding loadClass and findclass, you probably should have attended this talk and gave your opinions/suggestions/ideas
If I understood properly, I think there was a suggestion to add class loader info. to the ClassCastException (something like class-loader-class-name@identity-HashCode style string?) so that one can quickly see it is a class loader issue. Also, there were many questions on loading classes from jar files. Looks like there will be changes to class loader API and class loading in VM for JDK 7.
I'm QA guy therefore I know that there isn't anything like bugless software. However I don't get it why all the bugs appear at presentation time.
euhh..1100 peoples showed up for our Asynchronous Ajax for Revolutionary Web Applications session this morning...Good news for peoples that couldn't enter the room: Replay this Friday @ 12h10! And well, since you are there, why not registering for the Grizzly general session, which start Fri at 13h20! :-)
We admit it, we're proud of our design. But it's much more than just eye candy, as we believe that people work better when they work with something that pleases the senses. The pretty face of Ma.gnolia doesn't sacrifice usability, either. We place a premium on being easy to understand for people who don't know or care about how something works - they just plain want it to work.
Have you ever tried to compare external file editor or class file editor with other files/editors in Eclipse? Just try it...
There is NO way...
NO way? Not with AnyEdit ;-)
I've just released a new version:
Consider also you have two big pieces of code in the same editor which looks pretty similar, and you wish to compare them. Now you can just select one of the pieces, "Ctrl+C", then select the next one and choose "Compare With -> Clipboard" and get the Eclipse compare editor up!
Enjoy :-)
Sunday. It started as day off. We decided to have a small dim sum brunch at Chinatown. Jarda came up with the idea. And I have to thank him. It was unsharable experience. 


Then the registration for JavaOne started afternoon. The goodies look a little bit worse this time. Maybe it's because everything is eco or green this time. Wooden pen and eco light. Cool.
They served a special drink - Glassfish raspberry lemonade. The drink shined.

Just a quick note before we run from the office to pack our bags —
If you’re keen on meeting some of the people responsible for IntelliJ IDEA and TeamCity, come visit us at the JetBrains booth at JavaOne — booth #1312.
Hope to see you there!
You might also get an invitation for the JetBrains party to be held Thursday evening
Dave
Over the weekend, Microsoft announced that it was ending its bid to buy Yahoo!. The proposed deal, and its ultimate demise, was the subject of speculation for weeks