Introduction To The Client Side Java API

Ideas and tips for enhancing your TM1 application
User avatar
Alan Kirk
Site Admin
Posts: 6606
Joined: Sun May 11, 2008 2:30 am
OLAP Product: TM1
Version: PA2.0.9.18 Classic NO PAW!
Excel Version: 2013 and Office 365
Location: Sydney, Australia
Contact:

Introduction To The Client Side Java API

Post by Alan Kirk »

Click here to jump straight to the second instalment starting at part 10.
Click here to jump straight to the third instalment starting at part 17.
Click here to jump straight to the final instalment starting at part 23.

Part 1: Introduction

There's nothing intrinsically special about the number 1000, or multiples thereof. It's purely by historical chance that we use a base 10 numbering system which makes such numbers appear to have a significance that they really don't. Nonetheless, it feels somehow viscerally wrong not to acknowledge such milestones in some way. For others, I generally make mention of them reaching an x000 post count in the General forum. For myself, I like to write an article which goes beyond merely answering a question on how to format a websheet.

For my 4000th post I wrote a moderately lengthy article on using TM1's classic API in VB.net. At the time of writing it had garnered over 8000 hits which is somewhat extraordinary for a relatively niche area of knowledge.

(Though when it comes to "lengthy", you ain't seen nothin' yet.)

For this, my 5000th post, I'm sticking with a similar theme. However this time I'm writing something that most of you who know my past posts probably never expected me to write, given the antipathy that I've expressed toward Java in the past. And that is, of course, an introductory article on using the Java client API. And I emphasise that this is the client API, not the one used to do TI extensions. However you have to crawl before you can walk, and the client API is a decent place to understand Java skills first.

NOTE: For clarity and in line with the "Please don't ask for credit as refusal may offend" policy, while I'm happy to answer general questions if I can, please don't expect me to debug / write your code for you. I ain't getting paid for this, remember. And you ain't seen my rates.

Some Background To The Content
As this article was being drafted, I was approached by Cubewise to do a presentation at their Sydney conference for 2015.

(If there is a Cubewise conference near you, attendance is highly recommended. The amount of information about future and current developments that you get at those things is brilliant, as is the list of guest speakers.)

Accordingly this article was redeveloped to serve both purposes; it stands in its own right, but also served as the symbiotic basis for the speech that I gave last Thursday. And as the poor unfortunates who attended that speech know, I screwed up the timing of that speech royally by vastly underestimating the time needed to present it, meaning that the most interesting bits were omitted. (And indeed, I'm still not sure how I managed it quite that badly but one should never assume that the whole is the sum of the parts when it comes to time.) In this article I'm limited only by the amount of time you can keep your eyes open and how fast my fingers can move.

This article is intended to help you understand, in a way only touched upon in the speech, unfortunately:
  • When and why you might need to use an API-based solution (and why you may not want to);
  • Why you might use Java for that solution;
  • For those who have a background in VB/VBA (even if it's just in Excel) but limited or no understanding of Java, what to expect when making the transition to the Java language;
and it does that by providing a worked example which, as we all know, is the key to unlock this kind of thing.

It can't be a complete tutorial on either Java itself, or the TM1 API for Java, but at the end of this article you will hopefully at least a guide to get you started if you decide that this is something that you want to pursue.

If you come across terms that you aren't familiar with, take a look at the end of the article for a Glossary. I'll keep moving that post to the end of this thread with each instalment so that you can always find it.

About The Intended Audience
As with the presentation, as with my YouTube channel, as with any audience greater than about 10 people, the difficulty in writing articles such as these is that you can never be certain what the knowledge level or expectations of your audience are.

If you have no knowledge at all of coding principles, you will get lost in this article. Hopefully you'll still get a flavour of the possibilities, though. Also when my TM1 Bytes series reboots on the TM1Channel on YouTube in a couple of months you'll find that there is a parallel series called Programming Bytes which teaches the essential principles of programming to non-programmers; it's primarily intended for people who want to learn to automate Excel but does go beyond that.

The base level audience member is expected to have some knowledge of coding principles, probably from working with VBA in Excel, even if it's just using the macro recorder in Excel and fiddling around with the code to customise it.

If you consider yourself competent in VBA, so much the better.

If you've dabbled in the VBA TM1 API and want to know how to jump the gap to Java, great, but you don't need to have any previous API experience. Where I cross refer to equivalent functions in the classic API, you can just ignore those with no harm done.

If you're a capable Java coder but haven't played around with the TM1 API, you're possibly going to get a bit bored with the Java basics that I cover in the first few instalments but will hopefully still get some value out of it toward the end.

At the Conference I mentioned that this class of reader would be the ones throwing rotting produce at the podium and yelling "Your code sucks, Kirk". Surprisingly that didn't happen. (In part because I ran out of time to show the bulk of the code that was supposed to be the heart of the talk, as I touched upon in the previous section.) But as I mentioned then, it does highlight an important point; you don't need to be a gun Java coder to be able to develop working code.

I certainly don't regard myself as such; I only started getting seriously into Java in the last couple of months when I realised that the wind was blowing too strongly in the direction of Java in the TM1 world to bother resisting it. Similarly if you understand basic coding principles, and basic Java principles, you can produce useful code as well. Maybe not the most efficient code, maybe not the most elegant, but functional… and sometimes that's good enough.

And of course there was doubtless one final class of attendee at the Conference; the ones who made me think of an occasion when General George S Patton stood before an audience and said that they were there to "see whether I am indeed as big a sonofabitch as some of you think I am". I'm sure that they were not disappointed.

Still, the way that I completely screwed up the presentation (see above) means that it's not merely unlikely that Ben H will ever invite me back to do another one. I suspect that he in fact won't even let me through the audience door next year unless I sneak past by growing a suitably obscuring Joshua Chamberlain-style moustache, wearing a poncho and a sombrero pulled down over my eyes, and registering under the name Dr. A. Nony Mousse PhD from the TM1 Technical Academy in Malmo Sweden. Consequently for the foreseeable future you're stuck with articles like these, and the TM1 Channel.

About The Use Of APIs In General
An application programming interface (API) is exactly what the name suggests. You have an application – in this case, TM1 – and you write programming code to manipulate that application via an interface that the application exposes. This allows you to automate the application, and extend its functionality. This "what" part is easy.

The "when to do it" part is less so.

There are two words that I learnt in SQL Server development; "Fear Complexity". They should be engraved over your keyboard, whatever application you use or support.

When you decide to implement a solution using any API rather than the native functionality of the application, you instantly make the solution more complex. The solution needs to be documented, which often as not is something "for tomorrow". And when the documentation (if it's done at all) is needed, it can never be found. More complex solutions are harder to maintain and to upgrade. They're less transparent to end users and to people who take over ownership of the solution after you.

Consequently there needs to be a specific reason for using an API solution, and usually that reason is that you have a business problem which simply cannot be solved in any other way. We have occasional questions on the forum where people ask "can we do X, Y and Z using the API?" and the correct answer is frequently something like "Well yeah, you can... but why not use an action button instead?"

At the Conference Hubert Hijkers (the scarily smart Chief Architect of TM1 Server; ask him to talk to you for 45 minutes about the trie structures (see the Glossary if you think I've misspelt that) used inside the TM1 server some time and you'll see what I mean by "scarily smart") was demonstrating the new RESTful (REpresentational STate) API. He said something to the effect that previously, using an API solution was a last resort, but that the RESTful API may make it the first port of call. Make no mistake, I regard the RESTful API, now that I've seen it in action and have some idea of how to approach it, as being one of the most utterly freaking brilliant leaps forward for TM1 in years and I can't wait to use it in anger; I already have a bunch of things that I have in mind. But those are things that I simply cannot achieve with built in functionality.

And the complexity issue remains, especially as RESTful, brilliant as it is, probably isn't exceptionally friendly for non-technical users. I would maintain that the use of an API, any API, will remain and should remain a last resort. (But by all means, play and learn, and be aware of your options.)

The Example Used Here
The example that I'll be using is one where we need to obtain stock prices from a web service on the Internet and load those values into a cube. This needs to be done on demand.

Obviously you can't do this entirely within TM1 because there is no native web service data source for TI, and there probably never will be because there are far too many nuances and variations to such data sources anyway.

We could conceivably have an application that pulls the web source into a text file, and have that text file picked up by TM1, but you then need to ensure that the file has finished writing before attempting to load it. This necessarily introduces a gap between the time of the information and the time of availability.

In this case using an API solution seems to be a decent fit because although there are the workarounds that I described above, they don't really solve the key problems.

Of course, I'm fudging a bit here because I won't be using real time stock data. That costs money and I'm not spending it just to write an article. Instead I'll be using the Yahoo Finance API to suck down values from that service which, as with all free stock services, is delayed by about 20 minutes. However first it's free, second unlike Google, Yahoo is not known for arbitrarily pulling its APIs (do a search for Google Translator API Spring Cleaning if you want to learn new and exciting words of profanity from developers), and third, and best of all, there's a Java library that wraps around the Yahoo Finance API which returns the data as nicely formatted objects so that I can focus on showing you the TM1 stuff without having to also show you raw web access code and turning the later instalments of this article into "War And Peace, Volume XII".

The cube is made up of 6 dimensions:
  • SP_Exchange
  • SP_StockCode
  • SP_DateCode
  • SP_TimeCode
  • SP_UploadTime
  • SP_Measure
If anyone saw the Conference version of this during its all too brief screen time, yes, I changed the first dimension because it was bugging me. It was originally SP_Country, but the codes that come from the Yahoo Finance API are actually stock exchange rather than country codes even though I've yet to encounter multiple codes for one country. (P is Paris, L is London, AX (an abbreviation for ASX, presumably) is Australia, and so on.)

The stock code is of course the stock listing code on that exchange.

The date code comes to us from a Yahoo Finance Calendar object (explained later), and is converted into a text string to get the element name. I format it as YYYY-MM-DD and it represents the date that the data relates to.

The timecode comes from the same Calendar object and is formatted as HH:mm:ss, though in reality the source data doesn't appear to have a granularity greater than 1 minute. This represents the time at which the stock data applied, and it appears to represent my local time rather than the local time of the exchange. (The Java library that I use with the Yahoo Finance API seems to provide some methods for conversion but for the purposes of this exercise I haven't felt a need to explore that path.)

The Upload Time is a string in YYYYMMDD_HHmmss format (plus an element called Latest, which receives a copy of the last set of numbers uploaded as well) which indicates when the upload was done. It has a single consolidation called Total Loads which exists not because it has any real world utility, but simply to allow me to show you how to use the API to assign different weightings to elements in a consolidation. (It weights the "Latest" element as 1 and the other elements as 0.)

The SP_Measure dimension contains the elements Last Volume (the volume of stocks traded that day up to the time of the upload), Last Price (the last sale price of the stock), Last Offer (the lowest offer to sell the stock at that time) and Last Bid (the highest price a buyer was willing to pay at that time).

The final Java application can be called by a scheduled TI chore, and it will update the cube accordingly. It needs to:
  • Get the data from Yahoo Finance;
  • Scan it to see if there are new exchange or stock codes and create the elements and update the dimensions accordingly;
  • Scan for any date or time codes that haven't previously been used and add those to the dimension... yeah, OK, I'm slightly lying there. As you'll see when we get to the code, for simplicity's sake I'm not actually checking whether any of those elements exist first. I'm calling the Java API method to add them into the dimension regardless. Just as with the TI DimensionElementInsert function, if the element is already there it doesn't matter. However in a real world solution I'd probably check for the element first and update the dimension only if there were any new ones found to save the processing overhead of doing a dimension update. But a point I shall belabour a little in this article is that this is not designed to be a production system application. Not in the sense that it's unusable (it's in fact quite usable and fully functional and capable of being applied in the real world, as the results in the screenshot below show), but in the sense that at all times it was built with the purpose of teaching specific concepts rather than operational efficiency in mind.
  • Create the Upload Time timestamp code and add it to the Total Loads consolidation, weighting it as zero.
  • Load the values into the cube to both the current timestamp element and the Latest element.
What you end up with is something like this:
000010_StockCube.jpg
000010_StockCube.jpg (191.19 KiB) Viewed 70448 times


Why Java, Why Now?
Those who know my history know that I do not love Java. The issues that I have with Java as a language remain unchanged:
  • It's a case sensitive language. This harks back to the days when people were expected to serve computers rather than the other way around. (Java dates define "the epoch" as beginning on 1 January 1970, and that tells you all you need to know.) I regard case sensitivity as nothing more than syntax errors waiting to happen, and there's a particular pitfall that you can encounter with Java as we'll see. But to be fair to Java it's intended to be a cross-platform language running on operating systems (typically any descendant of the UNIX family) which are themselves case-sensitive. Consequently there probably wasn't a lot of choice in the matter.
  • Java is a language which is descended from the C language which means that each block of code is surrounded by squiggly brackets (called braces in Australia, and that's the term I shall use throughout) rather than actual words like If / End If or Do / Loop or Function / End Function as they are in VB. And for mine those squiggly braces are also syntax errors which are waiting to happen if you don't match them up properly, a fact that I was reminded of as I was writing the demonstration application for this article. Lovers of C languages and haters of VB claim that this difference makes VB "verbose". I say it makes VB "intuitive". PotaYto, potAHto…
  • Java is utterly, utterly awful at easily creating graphical user interfaces. I could have a full user interface with data grids and flashing lights done in VB.net by the time I had managed to draw the first panel in Java. And even when you do build them, they tend to be slow and chunky and clunky; just look at any of the Java applications (like Performance Muddler) in TM1 10.2. This is probably an unavoidable by-product of being cross platform, since operating systems handle graphical user interfaces in different ways.
  • Java has a well-established track record of security issues.
So why am I even looking at Java for automation of TM1? What's changed? For one thing, IBM has swung behind Java in a big way and given the cross platform nature of it, and the desire to expand the adoption of TM1 beyond Windows environments, that's not (for once) a criticism; I can understand where they're coming from.
  • First, the transition of the TM1 web server over to Java-based Apache Tomcat means that server side Java skills will be useful. Maybe not essential, but useful. In theory we may even be able to write additional code in Java to extend the user experience of your TM1 Web site, though with Apache Web still being essentially version 1.0 I don't think anyone is feeling suicidal enough to try that just yet. (And, admittedly, there will eventually come a point when "Prism" (or whatever it is finally called) will probably make TM1 Web as we know it redundant. But in the interim, Java skills will not go astray.)
  • Second, and perhaps most importantly, IBM has seen fit (probably for the reasons described above) to provide additional functionality to the Turbo Integrator language using Java extensions. Consequently if you're not at least familiar with the Java language you'll deny yourself the opportunity of using those extensions. And that means that your problem-solving toolkit as a TM1 administrator will be diminished. Let's be honest, there hasn't been a lot of development of the TI language since... ever. Java may (and I concede that this is speculative) become increasingly important in the development of TI code. The present implementation of this is quite limited (I recall one MVP describing it as something of a hack and a workaround), but again this is version 1.0 and there may be further development down that path.
    • Note: I repeat that this article is about the client side Java API, not the TM1 extensions which do not use the interface that I'll be showing you. They're a separate issue. But as I said, the client side Java API is a good place to cut your teeth on the language.
Other factors worth considering:
  • The GUI problem is immaterial if all you want is a command line solution. And in the case of much automation (including the case that we'll see here) a command line is all you need.
  • The Java API libraries are used as the basis for Android, which is continuing to gain market share hand over fist. Android itself doesn't use Java in the way that we'll be looking at, but it does (for the moment at least) use its language. The potential to leverage TM1 data on an Android platform should be obvious, though there are a couple of headwinds here. The biggest is the current legal stoush between Google and Oracle over the right to use the Java APIs in Android. If Google spits the dummy hard enough and decides to replace the Java APIs with native Android ones, that advantage goes out the window. Though given the current size of the Android codebase, there would be a lot of inertia to overcome in such a change. As noted above it's not like Google hasn't screwed developers over in the past, but such a change may hurt them harder than the developers and way harder than Oracle. Who knows, maybe it'll even let Windows Phone (which is not actually a bad product but is screwed by the fact that hardly any third parties bother writing apps for it) back into the race; stranger things have happened. The future is always an unknown, despite some people being paid quite tidy sums as "company visionaries" to predict it. (Incidentally, if anyone wants to pay me 250 large per year for sitting around on a bean bag wearing a skivvy, pontificating about how I see the future evolving and trying excessively hard to look cool, you'll find my contact details on my profile. I'll even bring my own dartboard.)
The security is more of a real issue, but we'll see why when we get to the explanation of how Java code runs.
User avatar
Alan Kirk
Site Admin
Posts: 6606
Joined: Sun May 11, 2008 2:30 am
OLAP Product: TM1
Version: PA2.0.9.18 Classic NO PAW!
Excel Version: 2013 and Office 365
Location: Sydney, Australia
Contact:

Introduction To The Client Side Java API

Post by Alan Kirk »

Part 2: The Basic Programming Concepts You Should Know

I'm going to assume that the reader has some grasp of basic programming concepts such as the following:
  • What's a statement? A statement is the simplest programming instruction that you can issue. For example y=y+7 is an instruction to take the variable y and add 7 to it. These types of statements are somewhat similar in all languages, though the syntax may change slightly. If you're familiar with statements syntax in any language at all, it won't take you long to get your head around them in Java. (Though the comparison syntax, the way you compare two values, is very, very different.)
  • What's a variable? In its simplest form, a variable is a name that's given to a value that you wish to store for manipulation in your code. A variable should be declared to specify the type or size of the value that is being stored, such as an integer, a long integer, a Boolean true or false value and so on. VBA lets you get away with not doing that and treating everything as a generic type called Variant, but it's very bad practice. Java requires you to declare a variable as having a specific data type, but you also need to be sure that you're using the correct type. It's far less forgiving (some may say less sloppy) than VBA in letting you get away with bad data type declarations.
  • What's a procedure / function / subroutine / method? A procedure is a named block of code. It's largely self-contained, though it may call other procedures to do some work for it. If you've been using VBA in Excel at all, even if you've only been using the macro recorder, you'll be familiar with this because any code will be recorded inside a Sub procedure, Sub being short for subroutine. In VBA a subroutine doesn't return a value to the program code that called it (if any). (That's a slight oversimplification when By Reference variables (discussed shortly) are taken into account, but the procedure itself returns no value.) VBA has another procedure type called a Function which does return a value. The term "procedure" is used in VBA to cover both subroutines and functions. In Java, there is no such thing as a subroutine, everything is a function. However functions that do not need to return a value are defined as returning a value type called "void". A "method" is simply a function that is defined as part of a class, which is something that we'll discuss in a moment. A method generally "does something" (loads some data, copies or moves files, calculates a return value, whatever) and may or may not return a value. In short whatever you call these, they're separate named blocks of program code which do something / calculate something / return some kind of value.
  • What is a condition? These are logical expressions which evaluate to a value of either true or false. Examples include If (7>2) (true, obviously) and While (n<500) where n is some variable (true only until n reaches 499, false after that). These expressions are known as Boolean expressions, and they are used to determine the flow of code. Specifically, if the expression is true, then do one thing. If it is false, do another. That's a branching statement. Or alternatively While something is true, keep doing a particular set of code. That's a loop. Both are regulated by whether the condition is true or false.
  • Ideally, what are classes and objects? This will be discussed in some detail in just a couple of headings from now.
User avatar
Alan Kirk
Site Admin
Posts: 6606
Joined: Sun May 11, 2008 2:30 am
OLAP Product: TM1
Version: PA2.0.9.18 Classic NO PAW!
Excel Version: 2013 and Office 365
Location: Sydney, Australia
Contact:

Introduction To The Client Side Java API

Post by Alan Kirk »

Part 3: A Different Way Of Thinking About Variables

As noted earlier, variables are named places to store and manipulate values as your code runs. Ideally the variables should be declared as a specific data type, as large as is needed to store the value but no larger. In VBA an integer should not be stored in a variable with a data type of Variant. In VB.Net it shouldn't be stored in a data type of generic Object, which is the approximate equivalent in that language, and the same is true for Java which also has an Object data type as its most generic type. (At this stage, for those on the lower part of the learning curve, let's just think of objects as "something stored in memory" and we'll get more specific very shortly.)

In normal Excel VBA, that's really as far as you need to think about variables.

But when you start using any of the APIs you need to view them a bit differently.

It's probably stating the obvious to say that the value of a variable is located somewhere in a computer's memory. For the computer to be able to read those values, write to them or change them it of course needs to know the location of the values. It does this by assigning an integer address to the memory location where the value will be found.

Some languages, notably C based languages, don't always work directly with the value but rather with the address at which the value is located. This concept is called a "pointer". The advantage of doing this is that when you are manipulating large blocks of data you can sometimes avoid having to transfer that data around in memory by updating the values directly at the address. The bigger advantage is that you can do pointer arithmetic to move very quickly from one stored value to another. VBA does not officially support pointers (nor does it generally need to), although there are certain undocumented functions that allow you to do that when you have no choice. (If I recall correctly (it's been a while since I looked), I used such a function in TM1 Tools to work around some issues that I had working with the clipboard when using the Bulk Paste function.) However the need to do so is not common, and many (most) VBA programmers will never come across it.

Both the classic and the Java API use a similar concept to pointers called Value Capsules.

(For clarity and the chronically pedantic: Note that I am not saying that these are pointers in the C language sense, merely that they serve, to some extent, a similar role albeit without (as far as I know) the fancy pointer arithmetic that you can do in C based language. And for clarity I should mention that Java itself does not, strictly speaking, have pointers either but has a methodology which allows some of the same functionality. But at this point we are going way, way beyond the scope of this article.)

Rather than representing a value, Value Capsules represent a reference to where the value can be found and you have to use other functions (in the classic API, functions like TM1ValBoolGet or TM1ValIndexGet) to retrieve the value from the location that the value capsule is pointing to. It's essential to be aware of when a function is returning a genuine value, and when it is returning a value capsule, because if you use one when the other is needed the classic API's method of handling the error is to metaphorically explode and burn.

Believe it or not the Java API also uses value capsules, but being an object oriented language they're a lot easier to work with. As will be seen in the example code later we can string a bunch of methods together to get the value capsule, and then the value "within" the value capsule (more correctly, the value that the capsule is pointing you to), in a single step.

(I clarified that last point because it is wrong and misleading to say that the value capsule truly "contains" the value, even though the word "capsule" seems to imply that it does. Obviously you can't embed an entire cube (for example), or long, descriptive text inside a long integer value, which is what a value capsule essentially is. The use of the word "capsule" is only a metaphoric one in the circumstances.)

When working with the Java API, if you're uncertain about what value comes back to you (that is, whether it's a value capsule or a real value), make sure to check the documentation. (Yes, believe it or not there is some. Not a manual; the closest to a manual is this article. But some references which you will need. I'll come back to that later.) The Java API seems to be less catastrophic in its crashes than the classic API is, but I have been able to make crashes happen from time to time if I work at it a bit. They usually take the form of the Java application just hanging.
User avatar
Alan Kirk
Site Admin
Posts: 6606
Joined: Sun May 11, 2008 2:30 am
OLAP Product: TM1
Version: PA2.0.9.18 Classic NO PAW!
Excel Version: 2013 and Office 365
Location: Sydney, Australia
Contact:

Introduction To The Client Side Java API

Post by Alan Kirk »

Part 4: About Primitive Values And Complex Objects... And Classes

Java is an object oriented language in which almost everything is an object, so before launching into it it's vitally important to understand the concept of the aforementioned objects (and the classes from which they are created) and, in contrast, primitive data types.
Data types fall into these two broad categories:
  • Primitives. These are single values. They contain no code, and represent only a raw, single value such as a single text character, a true or false value, an integer, a floating point number and so on. When I said that "in Java almost everything is an object", these were the exception.
  • Objects. Have you ever assigned a worksheet to a variable in Excel? That worksheet is an object. An object is sometimes defined quite rightly as being a combination of data and code; that is, a bunch of values which can be stored in it, and some programming code to manipulate those values. Let's look at the characteristics that an object has:
    • Properties (Fields): An object can store multiple values. In VBA these are called properties. In Java they're called fields. Think of a worksheet; it has a sheet name. That's a stored value. The colour of the tab is a value. It has an index, showing where it sits in the workbook. These are all properties describing the worksheet itself but the worksheet can also store other objects as property values; for example, the worksheet's cells collection is itself an object, and is stored as one of the properties of the worksheet.
    • Methods: Most objects have methods, which are procedures which "do stuff". Sometimes that's "stuff" is calculating things, sometimes it's copying, moving or deleting other things. For example a worksheet object will have a delete method to delete it from the workbook, and a calculate method to calculate its cells.
    • Events: In Excel objects can raise events. For example when you change the cell selection in a worksheet, this fires off the Worksheet_SelectionChange event which you can use to write code to handle.
I mention Events only for reference since with VBA (and even VB.Net) the "big three" for objects are always given as "properties, methods and events" but for our work with the Java API only the first two, Fields and Methods, matter.

Variables can contain either primitives or objects, depending on how they're declared.

Similarly either type can be passed as arguments to methods to tell the methods what data they need to act on.

When declaring the data type of a variable in Java, primitive data types will always be in all lower case. Objects (or more precisely classes, which are explained later in this section) should always start with an upper case letter by convention, though since that's not enforced by the compiler it may be possible for some third party classes to incorrectly start with a lower case alphabetic character.

The Dot Operator
In both VBA and Java properties/fields and methods of an object are accessed by using the object's dot operator.

For example in Excel VBA ActiveSheet.Name will return the name of the active worksheet object. ActiveSheet.Calculate will call the active worksheet object's "Calculate" method and recalculate that worksheet.

Java uses the dot operator in the same way for both fields and methods. The biggest difference from VBA is that in Java (and this will be the first of many, many times that we come across the case sensitivity issue) the names of the fields and methods should, by very strong convention, begin with a lower case letter and not the upper case letter that you find in VBA properties and methods.

Classes
All objects begin as classes. Classes are the templates which define the object's characteristics; that is, the object's properties (fields), methods and events. Somewhere in the bowels of Excel there is a class which defines all of the characteristics that any new worksheet will have.

In both VBA and Java you sometimes create a new object by using the new keyword to say that you want to create a new instance of the object from the class. Unsurprisingly this is called instantiating the class, and the resulting object is an instance of the class.

Once you have the new object, you can begin to use it by adding data to it and calling its methods to process that data.

In other cases you use one of the methods of an existing object to create the new object's instance. For example in Excel to create a new worksheet you begin by getting an object reference to the workbook that you want to create it in. If it's the workbook that you're running the VBA code in then you can use the ThisWorkbook shortcut to do that.

Then you get a reference to that workbook's Worksheets collection property. The value in that property is itself an object.

Then you call the Worksheets collection object's .Add method to add a new worksheet and, optionally, return a reference to the new sheet.

And all that can be done in a single line (plus one to declare the variable to hold the new sheet) like so:

Code: Select all

Dim wks As Excel.Worksheet
Set wks = ThisWorkbook.Worksheets.Add
Once you have the worksheet you can start doing things with it.
You can therefore consider the class to be like a cookie cutter; you use it to cut out the cookies (objects), which you can then modify and eventually consume.

Static Methods And Fields
Except that in Java the cookie cutter analogy falls down in one area; static fields and methods.

If you know the Static keyword in VBA (and not a lot of even experienced VBA coders do because it was one of those "it seemed like a good idea at the time" things), put it out of your mind. The Java keyword static (lower case) and the VBA keyword Static (upper case) are as closely related as Adam Baldwin and Alec Baldwin. (Not at all, that is.)

A static method in Java is one that can be called without creating an instance of the class itself first. It's almost as if part of the mythical cookie cutter was edible.

Similarly a static field (typically one whose value has also been defined as part of a class definition) can be called directly from the class definition. We will see examples of this in the sample code in later instalments.

Methods and fields which require an object to be instantiated before they can be used are called instance methods and instance fields. Typically the reason that these require an instance of the class to be created is because the object needs to be initialised in some way to be useful.

In the example code that will appear in later instalments, I've created a simple LogWriter class to output errors to the server's logging folder. To use that class it needs to be instantiated first so that the new instance can receive the path to write the files to. As a result the methods to write the log are not static, otherwise I'd have to be constantly passing in the output path rather than just doing it once when the object is created.

Conversely I have a StockReader class which obtains the stock prices from the Yahoo Finance site. It does not need any predefined values to run, and so the methods are static; they can be called directly from the class definition without ever creating a StockReader object.

The choice of whether to make methods static or instance is as much art as science and will be affected by considerations such as these. (And, in my case, because I needed a mixture of types to demonstrate the principles, something that will not affect you in writing your own code.)

Variables Used In Static Methods
A field's variable can be defined outside of any specific method in the class to make it available to either the entire application (if declared as public) or to all of the methods of that class only (if declared as private). The latter is the equivalent to a module level variable in VBA. However if those fields are referred to in a static method, the field itself must be defined as static. Have I lost you? Fear not, I've made sure that there's an example in the sample code and we'll see it in action when we get to that point.

The "final" Modifier - Constants For Java
I enclosed the word final in quotes not because it's a literal string, but because I didn't want you getting the idea that it was the last ever modifier. The modifier is the actual word final, all lower case.

If you're used to VBA you're probably used to the Const (Constant) keyword. It's where you declare a value once in your code, and cannot change it thereafter:

Code: Select all

Const SC_CONST As String = "XYZ"
Constants as such do not exist in Java. Instead you can declare a field as being final, meaning that it cannot be changed once the program is running. The equivalent declaration to the above in Java would be:

Code: Select all

final String SC_CONST = "XYZ";
Note that constants, by convention, should be named in all upper case in Java. The compiler won't spit the dummy if you don't but it will affect code readability.

Inheritance
Another important concept to understand in object oriented programming is inheritance.

Say you're writing a whole bunch of different objects, many of which need to have common methods or fields.

One option is to write the code for those into Every. Single. One. of your classes.

Option 2 is to write the code for those into a top level object and have the lower level objects inherit the same code. The inheriting class is called a subclass. The class from which code is inherited is a superclass. Unlike some languages Java allows only a single superclass per class, but that superclass can have its own superclass, which can have its own superclass, which can... keep on flowing quite some way up. In this way inheritance can cascade down several generations.

We see this at work in the TM1 Java API. In the java.lang code library which is the starting point of any Java development (and which we shall see in a future instalment), there is an object called, unimaginatively but descriptively, Object. It is the Big Daddy of all classes. Every class ever written ultimately inherits from java.lang.Object, whether directly or indirectly.

Take a look at the flow of inheritance in the TM1 Java API:
000020_Inheritance.jpg
000020_Inheritance.jpg (174.31 KiB) Viewed 70446 times
The first of the true TM1 Java API objects is the TM1Val object. Now do not let the name fool you into thinking that this is just for string and numeric values contained in a cube. This object is for all types of Tm1 "things", including references to objects like dimensions, cubes, etc, which may exist on a TM1 server.

We see that the TM1Object class inherits from that. This class relates to those objects that we described above; the class has the core features of any TM1 object whether it be a cube, an element, a chore, etc.

The individual TM1 classes such as TM1Cube, TM1Dimension, etc, then inherit characteristics from the TM1Object class.

Method Arguments And Signatures
As with any other procedures, class methods can have arguments which receive values when you call them. The arguments may be primitives, or they may be complex objects.

(Arguments are sometimes called parameters. There is a technical distinction between them (though that may be language dependent anyway) but for our purposes it's safe enough to use the terms interchangeably. Unless I slip up I'll stick to the term "arguments" in honour of the fact that the very first procedure in any Java console application has an argument named "args".)

For example the outputToLog method of the LogWriter class that we'll be seeing in the example code receives a String of text via the argument named stringToWrite:

Code: Select all

public int outputToLog(String stringToWrite) 
The "signature" of a method is the combination of its name, the number of arguments that it has, and the data types of those arguments. In the example above, this signature of the outputToLog method is the name of the method plus a single string argument.

Method Arguments By Reference And By Value
In VBA when you call a procedure and pass arguments to it, you can pass those arguments in one of two ways; by reference or by value.
  • If you pass By Reference, you are not passing the value as such. You are passing a reference to where the value is stored in the computer's memory. As we discussed under the heading titled "A Different Way Of Thinking About Variables", that means that the procedure that receives the argument can go to that address and modify that value. That of course means that the value will still be modified when the program returns to the original procedure. This is a useful way of returning multiple values from a procedure; the procedure itself can return one value and By Reference arguments can return supplementary values.
  • If you pass By Value, you are making a copy of whatever data is in the original variable and storing that copy in a different location in the computer's memory. That means that the second procedure will be modifying a different value. When the program returns to the first one, the original value will be unchanged.
In VBA (as opposed to VB.Net), arguments are passed by reference by default. If you want to pass them by value you have to explicitly say so. See the example below. Note that in both cases the same variable names are used through both procedures. That is immaterial. The name does not matter, which is why xByVal does not change. Only the memory location of the value matters, which is why yByRef's value does change.
000025_ByRefByVal_900.jpg
000025_ByRefByVal_900.jpg (132.56 KiB) Viewed 70343 times
In Java, all arguments are passed By Value, and there is no By Reference option. If you are coming from a VBA background, this means that you may need to change your approach when writing Java code. For example, you may decide to make more use of class fields to transfer data between methods.

Note: If you go searching the Interwebz you may come across occasional claims that primitives are passed by value in Java but complex objects are passed by reference. This is incorrect. It can sometimes appear that complex objects are being passed by reference (and I thought of explaining why at this point but it would be drifting too far off topic), but if you rely on that you are certain to greatly regret it, probably at a time when you can least afford to.

Overloading Methods
In Java methods can be "overloaded"; that is, there can be multiple definitions for a particular method, provided that each one has a different "signature"; a different combination of data types and numbers of arguments.

The outputToLog method described above is overloaded by having a second signature:

Code: Select all

public int outputToLog(String stringToWrite, int debugMode )
If you aren't interested in using the debug mode (which outputs certain event information to the console window if you're running the program from the command line) then you can use the method signature in the earlier section. If you want to use the debug mode, you can use the one above.

Similarly overloading can be effected by a method having the same number of arguments, but the arguments having a different data type.

For example in the TM1 Java API library the TM1Server object has two ways of getting a reference to a cube, both of which use a method named getCube:

Code: Select all

getCube(int index) 
and

Code: Select all

getCube(java.lang.String name)
That is, the appropriate method code will be used depending on whether you pass:
  • An integer specifying the index number of the cube. That is, the number of the cube in the server's cubes collection; or
  • A string representing the name of the cube.
In the classic API, which was designed for non-object oriented languages and therefore does not support method overloading, you need two completely different methods to do the same thing. The equivalent of the method signatures above in the old API were:
  • TM1ObjectListHandleByIndexGet to get the object by index; and
  • TM1ObjectListHandleByNameGet to get the object by name.
Overloaded methods reduce the number of method names that you have to remember and therefore simplify your coding.

They also overcome one of the limitations of Java. VBA has optional arguments; you can pass a value to the argument if you want to, but if you don't a default value can be used. Java does not have optional arguments, but overloaded methods are one way of getting the same result.

Constructor Methods
In Java, all classes have at least one constructor method. Constructors are methods which have the same method name as the class itself. They are used to run any code that's needed to initialise the object that is being constructed from the class. Constructors which have arguments allow you to (for example) set the initial values of fields, determine which blocks of code are executed when the object is created, and so on.

Constructors can be overloaded just as other methods can be. The LogWriter class in the example that you'll see in later instalments of this article has two constructors:

Code: Select all

public LogWriter(String projectName, String pathOfOutput)
and

Code: Select all

public LogWriter(String projectName, String pathOfOutput, int debugMode)
If you want to call the first constructor, you specify the project name and path of output only when you create a new instance. If you want to call the second one, you specify the project name, path of output and debug mode.

It's not mandatory to specify a constructor method when you write a class in Java, but if you don't then an implicit constructor method (one with no arguments) is still created by Java.

One Java developer (whose opinion I value highly) told me that he always specifies a no arguments constructor method when he writes a new class even if he doesn't put any code into it, just to make it clear to anyone who is reading it that he intends for nothing to be done when the object is created. I haven't done that in all of the classes you'll see in the example, but that's only because that code is a teaching example which needs to be kept clear of anything superfluous. In real world code, I'd always explicitly include at least one constructor.

Returning Values From Methods
Let's look again at one of the method declarations that we saw before:

Code: Select all

public int outputToLog(String stringToWrite, int debugMode )
In this, public is a keyword indicating that outputToLog is visible throughout the project.

The int keyword states that it returns a value which is of integer data type.

If you declare a method as returning a particular value type other than void, then it has to return such a value. It does this by using the return (all lower case) keyword followed by the value returned.

(And incidentally, don't put any code under the return keyword in the same code block or you'll get a "code inaccessible" error. You can however use multiple return statements within if/ else if blocks (explained later) to return different values depending on the conditions in play at the time.)

If you fail to put a return statement in, you'll get an error. Fortunately if you're using Eclipse, Eclipse will offer to handle it for you.

Primitives And Helper Classes
Now that we're all clear on the distinction between primitives and objects and what an object is, you need to know where the two collide in Java. And this is an essential reminder of why capitalisation matters so much in this language.

Every (as far as I know) primitive data type has a related "helper class" which can hold an instance of that type of data, but which also has a range of methods that you can use to manipulate the values of that type (such as converting them to formatted strings).

For example, for the long (lower case) primitive data type, there is also a Long (upper case) complex object which can do things such as convert the value to another data type. So if you get your capitalisation wrong, you can think you're getting a bunch of nice, light, low memory raw values, and instead you get a chunky memory consuming object. (Realistically in most cases it won't matter; on a modern computer the extra memory for the object would be a drop in the digital ocean. But if you have thousands or tens of thousands of values in a very large application, it may be a different matter.)

The only helper objects which have different names to their primitive counterparts are:
  • int, whose helper class is Integer; and
  • char, whose helper class is Character.
Classes In VBA
If you come from a VBA background you'll probably have noticed that VBA does contain class modules. If you've ever used them you will doubtless have learnt several new expressions of profanity. They are very rudimentary expressions of what a true object oriented class is. They lack inheritance. Worst of all they lack proper constructor methods which means that you have to do all kinds of dodgy and space and time warping workarounds to get initial values into them. In short, they're pretty pathetic examples of what a real class should be. This is as opposed to VB.Net classes which are, of course, the real deal since VB.Net is a true object oriented programming language.

But although it can use objects, calling VBA or classic VB6 an "object oriented language" would be like calling me "Channing Tatum" simply because we both have the same number of ear, nose, mouth and eye fields.

It would be an exaggeration at best. Pardon me for a moment while I do 150 sit-ups...
User avatar
Alan Kirk
Site Admin
Posts: 6606
Joined: Sun May 11, 2008 2:30 am
OLAP Product: TM1
Version: PA2.0.9.18 Classic NO PAW!
Excel Version: 2013 and Office 365
Location: Sydney, Australia
Contact:

Introduction To The Client Side Java API

Post by Alan Kirk »

Part 5: Data Types - Main Differences Between Java And VBA

When you declare a variable, you should specify the data type that the variable will have in VBA. In Java, you have to. That data type will either be a primitive or an object.

In VBA you are probably used to working with the following primitive variables:
  • Integers;
  • Long integers;
  • Double floating point;
  • Single floating point;
  • Booleans (true or false)
  • Bytes
  • Dates
  • Strings of text.
In Java you have a similar group of types.

Big Difference 1 - Strings
Strings in Java are not primitives. They are complex objects which have a very rich set of fields and methods. Another thing about strings is that they're immutable; once you have declared them you can't change them. "Hang on," I hear you say, "If I want to change "abc" to "abcdef" are you saying that I can't do it? Because that sounds wrong to me!" Well, of course you can. Kinda. But what actually happens in the background is that the original string object containing "abc" is destroyed and a new string object containing "abcdef" is created. When "changing" one string one time, this makes little difference. However if you are processing and manipulating hundreds or thousands of strings, you can start to take a pretty big performance hit with the constant destruction and recreation of string objects. For that reason there is another class called a StringBuilder, which is used to compile strings without needing to perform all of that overhead. That's getting beyond the scope of this article, so just be aware of it. Incidentally, this very same issue occurs in .Net, and the same solution is used.

Strings And Chars Are Different In Java
Do not mistake strings (multiple characters, enclosed in double quotes) with the char data type (single characters enclosed in single quotes). Chars are primitive data types in Java, Strings are not. Strings and arrays of chars are effectively interchangeable, but they are not the same thing.

Big Difference 2 - Dates
Dates are not primitives either. A Date (upper case) is again a complex object. It is not at all an index based value like the ones used in Excel and TM1, if you've read my article on using dates and times in TM1. (Though to be accurate, VBA (as opposed to Excel) dates aren't simple serial numbers either.)

There have been many different classes relating to dates in Java over the years, no pun intended. The version 6 and earlier Date type was replaced by a new Calendar class in version 7, which one wag on StackOverflow described as going from the "inadequate to the incomprehensible". Then in version 8 finally a third party library was adopted, which seems to have had pretty good reviews.

Don't get me started on the fact that some of the older date objects use a 1 base method for the day and a zero based method for the month, so that day 1 of month 1 is actually 1 February. Yeah, I'll bet that's never caused problems. And indeed you will get to see this in the example because I've needed to use one of the old Calendar classes because it's used with the Yahoo Finance API's Java library, which was written a while ago.

Big Difference 3 - Booleans
In VBA Booleans can be regarded as having a value of -1 for True and 0 for false. Do not make that assumption in Java, where there is no fixed numeric value; you must only use the keywords true and false for assigning values to and checking values of boolean variables. (Though an expression like if(bValue){} still works; that is, when doing a conditional test you need only check whether the expression being tested returns true, not compare it to the boolean value of true.) VBA users who were bitten by the fact that .Net changed the True representation of a Boolean from -1 to +1 should already know better than to assume what a value is, rather than simply use the defined constants.

Big Difference 4 - Arrays
To the extent that we'll be looking at them, arrays in Java are similar enough to arrays in VBA for the differences not to give you a brain ache. But you do need to be aware of the differences. In both languages:
  • They're ordered lists of values of the same value type;
  • They're immutable, meaning that the size of the array can't be changed once it's been declared. (This is true in VBA as well despite the Redim Preserve command, which really builds a new array of the new size and copies in the old contents, along with all the resulting processing overhead.)
There are differences, however:
  • Java arrays are complex objects. They have a bunch of methods and fields such as the .length field which makes getting information about them and working with them somewhat easier (and more powerful) than it is with VBA arrays, which are of course quite archaic by today's standards.
  • All arrays in Java must be base zero. (That is, the index list for the stored items begins at item zero, never at 1 or some other number.)
  • The declaration is different. You would use round brackets in VBA, square in Java. (In fact there are a couple of different syntaxes in Java; I'll just be showing one.)
  • When you declare the array the upper bound is different. In VBA you declare the UBound (upper boundary) of the array. So assuming that in VBA you have the lower bound set to zero by default, then this:

Code: Select all

Dim asMyArray(3) As String
Will get you a four element array consisting of elements asMyArray(0) to asMyArray(3).
In Java on the other hand this seeming equivalent:

Code: Select all

String myArray[] = new String[3];
specifies not the upper array boundary but rather the number of elements in the array. It will therefore provide you with only three elements, myArray[0] to myArray[2].

Something important, if a slight digression. In the TM1 Java API we need to work with value capsule arrays within the TM1Val object, as you will see when we create a new cube and when we create a view to update a value. These are not the same as standard Java arrays; in fact there is no connection with standard Java arrays. All will be explained in due course, but keep that in mind because if you confuse the two you're in for a world of frustration.

It's important to understand string arrays in Java because they are our first port of call when we start a Java console application. As you will see, the console application needs to have a method named main(), which is where the Java Runtime Engine (JRE) goes to when it starts running the code. And that method must have one argument, which is a String[] array (with an undefined number of members, until it receives the content from the command line). Traditionally that array is called args, so the definition looks like:

Code: Select all

public static void main (String[] args){}
The purpose of the args argument is to receive a comma delimited list of strings from the command line when you call the program. We'll see that in action later on.

Arrays in Java can have multiple dimensions just as VBA ones can, but the definitions are more flexible than in VBA. It's possible to create the second dimension's elements as arrays of varying sizes so that you end up with a skewed array, for instance. However first we have no need of that in our example, second although that's interesting I'm not sure it's wise, third if you have such a need it would probably be better to stop using arrays and start using objects contained within a proper collection object. (And Java has quite a number of good ones to choose from.) But fourth, we're drifting some way off topic there.

Initialising With (Big) Constant Values
Not so much a difference from VBA but rather an issue to be aware of when working with data types in Java. If you know how big a value a long integer can hold (hint: it's big. Really, really big) you may wonder what on earth is wrong with the code below:
000030_Initialise.jpg
000030_Initialise.jpg (28.43 KiB) Viewed 70448 times

That value is not outside the range of a long integer. So what's the problem? The problem is that unless otherwise specified in Java a constant number is treated as a regular integer. (Note the "of type int" bit in the error description.) And the value above is outside that range. To get around this you need to add an L at the end (so that the initialising value becomes 12345678901L) to tell the compiler that you mean the value to be treated as a long. (For once this is not case sensitive, but a lower case L looks too much like a 1 so most coders use the upper case version.)

You can run into similar problems with floating point numbers which may be interpreted as a double precision floating point rather than just a regular float. In such cases putting an F after the value is enough to get around the problem.
User avatar
Alan Kirk
Site Admin
Posts: 6606
Joined: Sun May 11, 2008 2:30 am
OLAP Product: TM1
Version: PA2.0.9.18 Classic NO PAW!
Excel Version: 2013 and Office 365
Location: Sydney, Australia
Contact:

Introduction To The Client Side Java API

Post by Alan Kirk »

Part 6: Java/Managed Code Versus Classic Code Part 1 - Writing It

In the olden days computer code was written to run on a single platform (a combination of processor class and operating system) only. To run on different platforms you had to modify the code.

Languages like the original C and Visual Basic 6 had their own compilers, so you would write your code in the format required by the language that you were working in, pass it to the compiler, see it error out, fix the errors, pass it back to compiler, see it error out, repeat the previous process cycle as many times as necessary with ever-increasing hair tearing and anguished groans of frustration until finally the compiler says "Yup, that's OK, here's your executable code."

Which you would then release to an unsuspecting public as version 1.0, and which they would then proceed to break in ways that you didn't even think were remotely possible.
000040_CreatePreManagedCode_800.jpg
000040_CreatePreManagedCode_800.jpg (120.47 KiB) Viewed 70345 times

"Managed" languages like.Net and Java take a different approach. You still write your source code. But then you have a compiler compile it into an intermediate binary form called bytecode.

In the case of Java, the first thing to do is make sure that you install the Java Development Kit, or JDK. (Remember this initialism, for we will encounter it multiple times through this article.) That contains the compiler, without which you can't (obviously) compile anything.

Ideally you also need a decent Integrated Development Environment (IDE) like Eclipse (which is freeware), which we'll meet later in this article.

You begin with a new project which has references to, at the very minimum, the JRE (Java Runtime Environment; discussed in the next section) system library. Old style compiled applications also have references to code libraries of course; nobody reinvents the wheel completely from scratch. But it's not that important for our purposes. The Java libraries are, as you'll see. That's in part because Java is a very minimalist language in terms of its core commands. Almost everything you do beyond a handful of commands will be creating objects, setting or reading field values, and calling methods. (Remember this well, for it's a point that you will see time and again in the course of this article.) And a big chunk of those objects, properties and methods will come from the library classes, and the biggest chunk (probably) from the core system libraries.

The library contains a bunch of .jar (Java ARchive) files, which are simply zip archive files with a different extension. Within those are "packages" which, although they appear as period delimited names and are often referred to in the code as period delimited names, are in fact a folder structure for storing related files together as we'll see. We'll be creating our own packages as part of the example as well.

Within those packages we have a number of different types of files but the most important of these are the .class files. Those are in binary (computer code) format. And they represent Java code that has been written long before you came along, and provide you with a whole bunch of Java functionality which you can then avoid writing yourself.

This, we will see when we get onto the actual example and create our first project. For the moment, take my word for it.

You write your own code (in which you will be, for the most part, creating the definitions of new classes and their properties and methods as we discussed previously) as plain text files with a .java extension.
000050_CreateManagedCode2_800.jpg
000050_CreateManagedCode2_800.jpg (176.17 KiB) Viewed 70345 times
It's already a given that you need to have references to the core libraries in your code (it won't run without them) but you do need to tell the Java which other libraries you want to use in your project. (In our case, the TM1 Java API library for one.) You do that by adding them to the build path, which is the set of paths that the Java compiler will search to find the libraries it needs. As we'll see when we get to the example, in Eclipse this can be as easy as right clicking on the library and selecting Add To Build Path.

When you've written your code you pass it to the compiler or easier still, have Eclipse do it for you. Eclipse in fact monitors your code and identifies any errors every time you save your code, so you don't even have to try to compile the code to know whether it will work.

The compiler then generates yet more .class files, this time your own .class files, compiled into your own .jars.
User avatar
Alan Kirk
Site Admin
Posts: 6606
Joined: Sun May 11, 2008 2:30 am
OLAP Product: TM1
Version: PA2.0.9.18 Classic NO PAW!
Excel Version: 2013 and Office 365
Location: Sydney, Australia
Contact:

Introduction To The Client Side Java API

Post by Alan Kirk »

Part 7: Java/Managed Code Versus Classic Code Part 2 - Running It

Classic compiled applications are generally installed on your computer. To run them you tell the operating system to launch them, they run in one or more threads in your computer's processor, and they interact with the operating system to do their thing:
000060_PreManagedCode_800.jpg
000060_PreManagedCode_800.jpg (66.26 KiB) Viewed 70345 times
Once again managed languages do it differently.

The computer on which you plan to run the code needs to have software installed that can read and run the bytecode in that environment. In.Net this is called the Common Language Runtime (CLR), and in Java it's called the Java Virtual Machine (JVM) or alternatively the Java Runtime Engine (JRE). Remember those initialisms as well, for we will also come across them many more times. (I'll lean more heavily to using JRE because that's the name of the folder that the software is installed in.) The JRE is named java.exe or javaw.exe for Windows.

The advantage of this methodology is that it's possible to write virtual machines which sit over multiple operating system and processor combinations. A virtual machine is written for each operating system (and yes, believe it or not, .Net is also capable of running on multiple operating systems, at least in theory). The virtual machine which is specific to that operating system/processor combination is installed.
When you want to run a Java application you (generally) fire up the Jave Runtime Engine (possibly by having the call included in a batch file to make it easier), which starts the Java.exe / JavaW.exe (as the case may be) executable file.
000070_ManagedCode_800.jpg
000070_ManagedCode_800.jpg (130.7 KiB) Viewed 70345 times

That loads up the core libraries, and your own compiled code. Since only the JVM/JRE is environment specific rather than your code, you end up with a cross-platform environment. What could possibly go wrong? This:
  • First, if the version of the JDK that the program was written in is later than the version of the JVM/JRE on which the program will be run, there may be compatibility issues. For example, version 6 of Java did not allow you to use strings in switch statements (the equivalent of Select statements for people who are coming from VBA). If you try to run the Java 7 application on a Java 6 runtime engine, things are likely to crash and burn.
    • By the way, you should be aware that Java uses a very eccentric version naming system. If you query the version of either the compiler or the run-time engine it will be displayed as (for example) version 1.6, 1.7 or 1.8. These are in fact shown as versions 6, 7 and 8 respectively on the downloads page.
  • Second, it may also depend on the source of the JVM/JRE executable. And yes, there are different JREs produced by different suppliers. The main one is supplied by Oracle but IBM, for instance, has its own one as well. In theory they should all be compatible, buuuut...
  • Also, I mentioned that Android uses the Java APIs to create its code. However the Android platform did not incorporate the Java 7 updates at the time they were originally released. Consequently if you wrote an application using Java 7 enhancements, you would most likely not have had a happy experience trying to run it on Android at the time.
  • Let's not even get into what will happen if you make operating system specific calls, including graphical user interface calls. (Not that these calls are made directly; they're made through the Java Native Interface (JNI, pronounced "genie" but without the three wishes), but one would have to be optimistic to assume that a cross-platform application will perform well if its graphics go beyond the basics.)
There are also obvious security risks with this model.

First, if you install a compiled application on your machine, then while you may not know everything it's doing you can at least make an assessment about whether to run that application based on whether you trust the creator of it. To install the application at all you need to have administrative rights.

In a managed environment it's actually the JRE / CLR or whatever that's actually running, and you have to just hope that whatever intermediate code is being passed into there is doing what it's supposed to be doing. And that someone hasn't found a way to sneak a .jar file into your JRE without you even knowing. Assuming that the JRE is permitted to run on your computer, you no longer have control over what code might be running within that.

This is even more of a risk with Java that is being run through a browser via the Java plug-in.

However managing risk is, unfortunately, part of the networked computing landscape. The more useful a computer technology it is, the more vulnerable it is to pond life who can't find anything useful to do with their lives beyond hacking into other peoples systems.

While I understand the point of view of the security expert who said "If you don't need Java, don't install Java", and while I can also understand the view of Apple who have ceased to install the JRE by default in their latest operating systems (though knowing Apple that's more about Java being "something we don't control" rather than it being a genuine security concern), if the risks are managed it can be worthwhile. This is especially so if you're running it on the server only for the purpose of implementing TI extensions, where the chances of it running nefarious code are minimal. (Or if it's just used to run the occasional API application like this one, though it would more typically be run on a client machine.)
User avatar
Alan Kirk
Site Admin
Posts: 6606
Joined: Sun May 11, 2008 2:30 am
OLAP Product: TM1
Version: PA2.0.9.18 Classic NO PAW!
Excel Version: 2013 and Office 365
Location: Sydney, Australia
Contact:

Introduction To The Client Side Java API

Post by Alan Kirk »

Part 8: Transitioning From VBA To Java

This section lists the most important issues that you need to get your head around when making the transition from VBA / classic VB to Java. Even if you aren't doing that, it contains some useful information about the Java landscape.

Standard Modules Are Dead
In VBA most of your code is written in standard modules. These are embedded in the containing document, such as an Excel workbook or a Word document. These typically contain subs, functions and variables. The modules are given a name but you don't need to declare the start and end of the module; once created, modules in VBA simply "are" and need nothing further for you to start using them.
000080_ModulesVBA.jpg
000080_ModulesVBA.jpg (88.68 KiB) Viewed 70447 times
Of course this is quite different to VB.Net where you do need to declare the start and end of modules, whether they are classes or standard module. (Not that there's a lot of difference between the two in VB.Net.)

In Java, all code needs to be contained in classes; there is no such thing as a standard module. As mentioned previously, class definitions are plain text files which are contained within a Java project.

A project is typically contained within a folder. In the example below, "Post5000" is the project. Below this there will be (at the outset) a src folder for your source code. This folder is called the "default package", and should never be used to store source code. Instead you create your own "packages" (which are in fact folder paths). As we can see below there is a folder called com (which should not store anything other than the next folder), below which is a folder called yourcompany (which should also not store anything other than folders), below which are the folders for each group of classes which store your .java source code files.

This is called reverse domain name notation. Since the package location is part of the class definition its purpose is to uniquely identify the code that has been generated at your workplace by using the (unique, obviously) domain name for your website so that if there are two (say) WebUtilities classes in your project, one created by you and one by an external party, it will be possible to distinguish between the two.

Although the packages do represent folders, as we'll see in just a moment within the code itself every reference to them strings the folder names together and separates them by periods. So in this example you would see something like package com.yourcompany.logwriter.
000090_Folders.jpg
000090_Folders.jpg (64.79 KiB) Viewed 70447 times
More Braces Than Gordon Gekko's Wardrobe
Unlike a Visual Basic module, you need to specify the start and end points of the class by using the class keyword (lower case) and opening and closing braces. (You can have multiple classes within a .java file but only one can be public (exposed to the entire project), and it must have the same name as the .java file). The opening and closing braces of the MainCode class are shown in yellow below.
000100_Class.jpg
000100_Class.jpg (125.32 KiB) Viewed 70447 times

Classes aren't the only components defined by sets of braces; any code blocks must have their own sets. In the illustration above we have several groups of code that constitute blocks.

Procedures
There are two procedures; main and myCode. Both of these are methods, and the code for methods must be contained in braces. For main we have an opening and closing brace on the lines highlighted in green.

For myCode they're on the lines highlighted in blue.

There are varying styles of brace placement but the one above seems to be the most common; the opening brace appears immediately after the end of the keywords that define what type of code block it is, and the closing brace appears indented to the same level as the start of those keywords. As an example, note that the closing brace for both methods appears indented at the same location as the process declaration of public static etc.

If, While, For blocks
In the main method we have a while loop (with "while" always in lower case). The while is followed by a conditional test; whether the value i is less than or equal to 8. (I'll discuss this further shortly.) The code that is executed if that condition is true is surrounded by the braces which are highlighted in purple.

In VBA a While would end with a Wend statement (which makes identifying the end point of the code block much easier if you have a bunch of them nested), but in Java you have no such luxury. All code blocks end with a simple closing brace. You can (and as we'll see I often do) add a comment after the brace to show what exactly it's supposed to be closing off.

In myCode we have two code blocks. The first is an if block (and that's a lower case if; if you type it as "If" you'll get a compile error) which is executed if the conditional test i equals 7 is passed. Again I'll come back to the syntax in a moment. The open and close braces are highlighted in red. The second is an else if block (two words, all lower case) which is executed if 1 does not equal 7. (It's rather redundant to do an else if here since if i does equal 7 it will be caught by the if block, but again, this is a training exercise.) The open and close braces are highlighted in pink.

Had we had a for loop (which I omitted for reasons of relative brevity... I said relative), it would do the same thing.

I trust that you're seeing the pattern now. Whether it's if, while, for or whatever, you do the conditional test (which is contained in round brackets), then open the brace, then do the code that relates to the test, then close the brace.

Lunch is for wimps, greed is good, braces are bad but we have to live with them. Gordon would be proud of you.

Braces For Code Blocks, Semicolons For Code Lines
Although braces are used to define the beginning and end of code blocks, the individual lines of code within those blocks need to be terminated with semi-colons, just as happens in TurboIntegrator. Most of the code blocks in the illustration above are made up of single code lines, but that would be rare outside of a training example like this. (Even here, the while code block has two lines.) The semi-colons are used to tell the compiler when one statement ends and the next one begins. Forget them, and just with TI you'll get an annoying compile error message.

You don't add semicolons after braces, as I've seen some new users who have yet to get their heads around code blocks do. Remember the heading of this section and you'll be fine.

Capitalisation
Again with the capitalisation, but trust me, in Java, it matters.

Note that the package name in the graphic above (com.yourcompany.package) is in all lower case, the class name (MainCode) starts with a capital letter and is then in lower case except when I'm starting a new word, while the procedure names (main and myCode) start with lower case letters. Most normal methods will use mixed case in the middle, like the myCode method. (This is known as camel case, since it looks like there's a hump in the middle.) The main procedure is an exception because it is the starting point for any console application and must have that name, and exactly that name.

These are very strongly held convention in Java.

While the compiler won't normally throw a compile error if you violate them, experienced and dedicated Java coders may indict you for a heresy and burning at the stake for possible witchcraft is not outside the bounds of possibility.

By following the principles, if you see something starting with a capital letter it can usually be assumed to be a class.

If you see something starting with a lower case letter, it can be assumed to be a method (function) or field (property).

Operators

String Concatenation (use + not &)
Java uses the + operator instead of the ampersand (&) to link two strings. Yes, that's the same operator as for addition. Also, there's an implicit data type conversion when you use this. So suppose that we had a String variable sMyString which has a value of "TM", and an integer variable iMyInt which has a value of 1. Then this:

Code: Select all

return sMyString + iMyInt;
isn't trying to add 1 to the text; instead it's turning 1 into text, and putting it on the end of the sMyString variable.

The result you'll get is "TM1" (what else?)

The Bang (!) Operator
The exclamation mark, sometimes called the "bang" operator for what should be obvious reasons, is used in various contexts for "not". If you place it in front of a boolean variable, especially in a conditional test, it essentially "flips" the value.

Code: Select all

if( bResult ) 
would execute the ensuing code block if bResult was true.

On the other hand

Code: Select all

if( !bResult ) 
would execute the ensuing code block if bResult was false.

The Comparison (Double = and the .equals method)
This is one that almost certainly will catch you out from time to time, no matter how careful you are. (Unless you work almost exclusively in in Java or other C-related language, anyway.)

In VBA you use a single = sign to assign a value to a variable, or to compare two values. The one that is intended is always clear from the context.

In Java, the equals sign is still used to assign a value. For example:

Code: Select all

String sMyString = "";
(Note too that you can declare variables and initialise them in a single line, just as you can in .Net, but not in traditional VBA. This is a welcome difference.)

However to do a comparison, the syntax needs to be:

Code: Select all

if(iMyInt==8)
That is, you have to use a double == sign. If you put it as iMyInt=8, you're going to get a nasty surprise, if the code compiles at all.

However, do not, and I'll repeat that with appropriate emphasis, do not use the double equals sign when comparing strings. The reason is that strings are complex objects. As I keep repeating, because you need to hear it. Remember it. Own it as a concept.

If you use the double equals you are essentially asking whether one string object equals another string object, not whether one string value equals another string value. Sometimes you'll get the right result and sometimes you won't.

Check this out:

Code: Select all

		String s1 = " Mulder's Apartment Is 42";
		String s2 = new String("Mulder's Apartment Is 42");
		if(s1==s2){
			System.out.println("They match");
		}else{
			System.out.println("They don't match");
		}
s1 is declared using the shorthand syntax. s2 is declared using the longhand full syntax. (I mentioned previously that when you create a new object, using the "new" keyword is one way of doing it. The s2 declaration illustrates this. But because creating a string is such a common requirement the language lets you take a shortcut and simply assign a value straight to the new string object without explicitly instantiating it first. Functionally, though, the two expressions are identical.)

Want to bet what you'll get as a result? Correct, they don't match.

If I change s2's declaration to:

Code: Select all

String s2 = " Mulder's Apartment Is 42";
but leave everything else identical, then I'll get the result "They Match".

How do you avoid this?

The string object has an .equals method, which you should always use when comparing strings, like so:

Code: Select all

		String s1 = "Mulder's Apartment Is 42";
		String s2 = new String("Mulder's Apartment Is 42");
		if(s1.equals(s2)){
			System.out.println("They match");
		}else{
			System.out.println("They don't match");
		}
This will always return the right value.

This is especially important if, for example, you're looping through a list of TM1 objects like elements and checking their names to see whether the current one matches. You may be in for a very frustrating time indeed tracking this one down in such a case because it's such an obscure issue.

Does Not Equal (!=)
Just as you have to get used to using the == to check equality if you're from a VB background, you have to get used to using != rather than <> for the "does not equal" expression. You'll see both in action in the myCode procedure illustrated above.

Increment
If you've moved to VB.Net this won't be news to you but coming from VBA it's most welcome.
Instead of tediously having to type i = i+1 to increase your i counter, you can simply call it as

Code: Select all

i++;
There's also

Code: Select all

i--;
if you want to decrease by 1, plus a few others.

(But as this isn't a manual on Java as such, that's left for the reader to investigate further.)
User avatar
Alan Kirk
Site Admin
Posts: 6606
Joined: Sun May 11, 2008 2:30 am
OLAP Product: TM1
Version: PA2.0.9.18 Classic NO PAW!
Excel Version: 2013 and Office 365
Location: Sydney, Australia
Contact:

Introduction To The Client Side Java API

Post by Alan Kirk »

Part 9: Exceptions, Try / Catch / Finally blocks, and Throws Statements

Java is a lot more particular when it comes to runtime error (exception) handling than VBA ever was. In VBA you could just take the risk that everything would go smooth and let it blow up at runtime, throwing a cryptic Visual Basic error message into the user's face. Sure, the user will hate your living guts for doing so, but there was nothing technically to prevent you from doing it.

There is in Java.

The Old School
If a runtime error occurred in VBA (a file didn't exist, a value was divided by zero, whatever) then an object named Err was raised. Despite being one of the few objects to natively make an appearance in VBA, Err was a pretty rudimentary object. It had a handful of properties and a method to raise a new Err object, and that was about it.

VBA allowed you to specify three commands relating to errors:
  • On Error Goto xxx, where xxx was a label in your code which your program would be redirected to to deal with the error;
  • On Error Resume Next, meaning "live life on the edge and pretend that the error never happened, and just go on to the next line of code"; and
  • On Error Goto 0, meaning "turn off all error handling because I want my users to hate my living guts as much as I hate theirs".
In addition the Resume statement allows you to resume at a particular line number, another label, the line that threw the error or the line after that.

It wasn't an entirely bad system but did result in a certain amount of bouncing around from one place to another in the code.

The New Model
Almost all modern languages - the .Net languages, T-SQL for SQL Server, Java... use a system that uses (in most cases) Exception objects and (in all cases) some variation on try / catch (lower case in Java) blocks of code.

Exceptions are complex objects with a large number of properties and methods. When a runtime error occurs, an Exception object is created. Exception is typically a superclass which has subclasses relating to specific types of exception, such as IOException, BadLocationException and so on. These can have further sub-classes; for instance the IOException one has subclasses of CharacterCodingException, FileNotFoundException, and a bunch of others. And of course, you can create your own Exception classes as sub-classes as well, though it's generally recommended that you do this from one of the lower level ones and not directly from Exception.

You should normally always use specific exception classes, not the generic Exception one. I've used Exception in the example only because it's a training exercise and there's a need to keep it simple. I would never do it in production, and nor should you.

In your code you use a try code block (in which you execute any code that you know may potentially cause an exception at runtime) followed by one or more catch blocks, each of which is passed an Exception object or one of its descendants. (You can use different catch blocks to handle different types of Exception objects, though I don't demonstrate this. In VB.Net you need to go from the most specific Exception to the most general in doing that, and I imagine that it's the same in Java though I admit that I haven't looked at this.)

The group of blocks can end with a finally block which does any clean-up, such as closing any files that have been opened.

The try/catch blocks can be nested, so that you can end up with something like this:

Code: Select all

try {
	//Some code that may throw an exception.
	//Some more that may throw an exception.
	try {
		// A specific few lines of code that may throw a fixable exception.
	} catch (Exception e) {
		// Fix that exception and carry on
	}
	//And yet still more that may throw an exception.
} catch (Exception e) {
	// Something to handle an exception, such as logging it or whatever
}finally{
	//Close any open files, or what have you.
}
Note the following:
  • Each block (each try, each catch, each finally) has its own set of braces to designate a code block.
  • We have a nested exception here. It's quite possible to do that a few layers deep, but the deeper you go, the harder your code becomes to follow.
  • Each catch block receives its own Exception object.
  • The finally blocks may not have a long life expectancy. In the most recent version(s) of Java (I can't recall whether this came in in version 7 or 8) a new syntax called "try with resources". This involves opening any files, URL connections or whatever in a block just after the try keyword. Any resources created in there will be automatically closed when the block goes out of scope (that is, when the JRE is finished with it) without the need to explicitly do so in a finally block (or anywhere else for that matter).
The try/catch syntax is a far more linear approach than On Error Goto. On Error Goto worked well for the kind of errors that caused you to need to cancel the entire code, less well for things that may need to be fixed and tried again. Those could result in you bouncing around from one point in the code to another resulting in the dreaded "spaghetti code".

Throwing Exceptions
You can throw your own exceptions by simply stating:

Code: Select all

throw new Exception("Description");
(Again, though, you really should not be throwing generic Exceptions rather than specific ones.)

If this appears in your catch block, what you're effectively doing is throwing the Exception back up the call stack until it reaches a point at which you're comfortable dealing with it.

The throws keyword
There is one caveat. If you throw an Exception out of the method (that is, you don't handle it within a catch block but rather let it be returned (or use throw to let it be returned)) to the calling procedure, then Java will require you to add the throws keyword to the method definition and specify each of the possible exception types can be thrown. If you fail to, you get a compile error (which again, Eclipse can automatically fix for you).

This serves as an early warning system for the calling procedures. Which leads me onto this fact as well...

If you are calling a method that itself is defined as throwing exceptions (regardless of whether it's one of your own, or one that's in a library), then the call to that method needs to be wrapped in a try / catch block, or you have to modify your code to specify that your method throws the same Exception(s).

The TM1 API Does Not Throw Exceptions - You Need To Look
While you need to be aware of error handling because you won't be using the TM1 Java API code in isolation (you'll almost certainly need to be doing other things as well such as opening files or internet connections, which may throw Exceptions), the TM1 API itself doesn't seem to do that. Instead the various classes that you'll encounter have an isError method (inherited from the TM1Val class), which is a Boolean (true or false) value telling you whether the object has an error. Accordingly if you attempt to get a reference to a dimension that doesn't exist, that action won't generate an Exception that you can catch in a try/catch block; rather the isError method will return True. It's therefore up to you to make sure you test that value as and when you need to, and manually throw any exceptions yourself. We'll see plenty of examples of this when we get into the code.
User avatar
Alan Kirk
Site Admin
Posts: 6606
Joined: Sun May 11, 2008 2:30 am
OLAP Product: TM1
Version: PA2.0.9.18 Classic NO PAW!
Excel Version: 2013 and Office 365
Location: Sydney, Australia
Contact:

Introduction To The Client Side Java API

Post by Alan Kirk »

Part 10 - Installing Java

In light of the earlier content you probably know that you need two things to start writing Java code:
  • The Java Runtime Engine (JRE) / Java Virtual Machine (JVM), containing the java.exe / javaw.exe executables (plus a bunch of other stuff, obviously); and
  • The Java Development Kit including, amongst other thigs, the javac.exe compiler.
You may be aware that Java was originally developed as a (sorta) open source project by a company called Sun Microsystems way back when. Sun was bought by the database company Oracle, and that's where you need to go to download the "benchmark" editions of it. (As I said, some other companies do make their own JREs, but I'd recommend sticking with the "official" one while learning at least.)

The page that the download appears on constantly changes as new versions come out and Oracle updates its web site, so I'd recommend going to the Oracle home page ( http://www.oracle.com ) There, you will find a "Downloads" link as we see here:
000110_OracleHomePage_800.jpg
000110_OracleHomePage_800.jpg (92.78 KiB) Viewed 70344 times
Before you click on that, let me give you some background.

First, as I mentioned Java has a seriously whacked version numbering system. When Java version 2 was released the internal version number (the one that you get if you go to a command window after installing Java and typing the command java -version ) was set not as 2.0, but as 1.2. Similarly Java 6 would report itself as Java 1.6, Java 7 as Java 1.7 and Java 8 as Java 1.8. Why they chose to do this will remain an eternal mystery, but just be aware of it to avoid confusion.

At the time of writing Java 8 was current though Java 9 was coming down the pipeline. I have no idea how far into the future you may be reading this so I have no way of telling which version will be current then. I'll be using the most current sub-version of version 8 / 1.8, though as you'll see I do have Java 7 on my machine as well. (It is possible to have more than one version of Java installed on your machine at the one time, in separate installation folders obviously.). The links on the Oracle home page will take you to the most current version (though when you hit the downloads pages you'll have links to a couple of older releases), but the most current one is the one that you should use.

There are multiple "flavours" (I hesitate to call them "versions") for fear of amplifying the confusion. They are:
  • Java Standard Edition (SE): This is the flavour that you use to create desktop applications, console applications and so on. It's the one that will do us just fine for creating client side TM1 API code.
  • Java Enterprise Edition (EE): This is the one that you use on the server side if you're running a Java based web server like Apache Tomcat... which of course TM1 is, but that is irrelevant for our purposes because we are working (at present) on the client side, not the server side. If you want to explore server side Java (and you probably will if you're serious about it) you will need to install EE at some point but I'd still recommend installing SE on your desktop/notebook first just to keep things simple. (There is nothing as far as I'm aware that SE can do that EE can't but I'm not sure whether the EE version comes with all of the frameworks (like Swing) used in desktop application development.) Consider EE to be more of a "superset" of Java SE rather than a completely different language. It's worth noting that both the Java for Developers link and the Java SE link in the screenshot above will take you to the Java SE page, though the SE page has links to other flavours like EE down the side.
There are various other flavours as well such as Java ME, which is the micro edition intended for embedded devices and small resource mobile devices, but that's not of interest to us at present.

Having chosen your version (whatever the latest is) and your flavour (SE recommended for learning purposes), you then choose the download package and again there are two choices:
  • The Java Runtime Engine (JRE / JVM) only, which you need to run Java bytecode; or
  • The Java Development Kit (JDK), which is where the "for Developers" and "Java SE" links will take you.
You want the JDK, because it includes the JRE install. More specifically, once you start the JDK install it will spool off a separate install to take care of the JRE.

The download pages will probably look something like this (though the specifics may change), even in the distant future:
000120_Oracle_JDKDL_800.jpg
000120_Oracle_JDKDL_800.jpg (215.36 KiB) Viewed 70344 times
Looking around:
  • On the left, as I mentioned, are links to the other flavours of Java.
  • Down near the bottom is a "What Package Do I Need" section, which tells you essentially what I have above but with far less style, grace, wit and all round awesomeness.
So looking at the two big buttons on the top, what is this "NetBeans", exactly?

It's Oracle's Integrated Development Environment (IDE) for developing Java code. I've never hear anyone damn it as a curse upon humanity, but nor have I heard much effusive praise. It seems to be a perfectly decent IDE but not one that will rock your world. If you're following along with me you don't need it because we'll be using Eclipse instead, which is a very good IDE indeed and has a pretty large user and enthusiast base. Accordingly just go for the big "JDK" button.

That will take you to another page where you can choose your operating system. As I mentioned Java is cross-platform, so there will be many flavours to choose from. At the time of writing:
000130_OracleDLFlavours_900.jpg
000130_OracleDLFlavours_900.jpg (296.25 KiB) Viewed 70344 times
Make sure that you select the "Accept Licence Agreement" button after you have read the licence agreement as thoroughly as we all do when installing software, then click on the one that relates to your operating system which, for the readers of this article, will almost certainly be either Windows x86 (for 32 bit Windows) or Windows x64 (for 64 bit Windows). I would hope that anyone reading this wouldn't need to be told this but if you aren't sure which version you're on find the Computer / My Computer icon or Start Menu entry, right click on it and select Properties.

This will start downloading the package to your computer, and I suggest that you opt to save it in your Downloads folder or some other place on your local machine, not to a network location. (Some sites have policies in place which can stuff up installations from network locations.)

Once you download the installation package, go to the folder containing it, right click on it and run it as Administrator. That will launch the JDK installer which will launch a sub-installer to install the JRE. You can install it anywhere you want, but the default installation will be in C:\Program Files\Java.

Although not essential, stick to this if you can to make your life easier. If you do, you should see something like this. Note the presence of separate folders for the JRE and JDK (yellow). Beware of the fact that for some reason the install also puts a copy of the JRE into the JDK's folder (pink). This can be a problem if you configure the JDK's copy of the JRE rather than the "real" one that is used to run your operational applications. (What sort of configuration? Like adding the TM1 SSL certificate to the Java keystore, as we'll see. Add it to the wrong one and your code will error.)
000140_Installation.jpg
000140_Installation.jpg (77.37 KiB) Viewed 70348 times
You can already see Java's eccentric naming system in play; my installation of Java 8 appears in folders named 1.8.0_51 rather than "8". You'll note that I've left my older version 7 installs in place as well. (If I remember correctly I manually specified the "jre7" folder name, though these days I would prefer to go with the defaults.)

As you can see that some software producers keep to a consistent file naming pattern. And let you run parallel versions. And don't expect you to manually uninstall old versions before you install a new one. Let us say no more about that. For now.

The bin folder of the JDK naturally contains the binary code such as the compiler (named javac.exe), as well as a copy of the runtime engine (named java.exe and javaw.exe). The bin folder of the JRE contains only the runtime engines.

The Path Environment Variable
How does Windows know where to find files that it needs to run, or code libraries (.dlls) that it needs to reference? I expect most of those reading this know that, but for the benefit of some who don't...

When you run a program from a shortcut on your start menu or desktop, the shortcut contains a full path to the program file. No problem.

If you're typing the name of an executable into the command line, and your current path is where that executable file is located, also no problem.

But if none of those apply? The answer is environment variables.

Most readers are probably familiar with those. For those who aren't, they're strings of text which are stored inside the Windows Registry, and which contain information about the current Windows environment. The operating system can look up those values as it needs to. Other operating systems have a similar system for similar information.

One of the most important environment variables is the Path one, which provides a semi-colon delimited list of directory paths that Windows searches if it's told to run a particular application or call a particular code library without being given the full path in the call.

For example, when you use the classic API within VBA you call a lot of library functions which are declared in a standard module called tm1api.bas. (That module is supplied as part of the TM1 installation, and needs to be added to your VBA project.) An example of such a declaration is:

Code: Select all

Declare Function TM1BlobClose Lib "tm1api.dll" (ByVal hPool As Long, ByVal hBlob As Long) As Long
But note the absence of a path to tm1api.dll. You could include it, if you don't mind changing it for any machines on which TM1 is installed on a different path. (Remember that you don't have to install TM1 on the default path, and even if you did Windows and VBA don't know what that path is, especially as IBM seems to change it every second version.)

The solution is to add the path of the API libraries to the Windows Path environment variable.

Had TM1 come with a decent installer, it would do that automatically. It doesn't, so for the classic VB API you have to do it manually. We'll do something similar for the JDK, but out of convenience rather than need.

You can modify the environment variables by typing the word environ into the search box of your start menu. (If you have one; if you're on the cursed waste of space that is basic Windows 8 and don't, get one by downloading Classic Shell.) Typing environ on the tile-infected home screen should still get you to where you need to go, albeit more slowly. Alternatively you can go to the Control Panel and get there that way.
000150_EnvironVars.jpg
000150_EnvironVars.jpg (116.85 KiB) Viewed 70348 times
Click on the Edit the system environment variables link that comes back from your tab, which will take you to the Advanced tab of the System Properties dialog. Click on the Environment Variables... button to bring up the dialog. Select the Path environment variable and click [Edit...]

I prefer to copy the Variable Value field out to Notepad ++ so that I can see what I'm doing when I modify it; this is a ridiculously tiny window for what is, typically, a very long text string.

I usually add the path to the JDK compiler (not the JRE; we'll come back to that) to the Path environment variable. I do that by making sure that there are no paths to older versions of the JDK in the string, and remove them if there are. (This is why Notepad++ comes in handy; you really can't see this in a small textbox.) In the screenshot above I still had a reference to the old 1.7 JDK , so I'd remove that. Then, after making sure that the previous path in the string had a semi-colon after it, I would add (in my case)

Code: Select all

C:\Program Files\Java\jdk1.8.0_51\bin;
Note the inclusion of a semi-colon after the entry. It's not essential for the last entry in the Path variable but is good housekeeping so that you don't have to worry as much about it if you put future values in.

Once that's been done Windows will be able to find the javac.exe compiler without needing to be told exactly where it lives. This is probably not essential when you're using Eclipse (I've known at least one person who wrote Java code without changing his environment variables), but it certainly can't do any harm.

The JAVA_HOME Environment Variable
There's another environment variable which may not be essential for what we'll be doing, but which I know is needed for some more advanced Java code so we may as well deal with it now.

The name of it is JAVA_HOME, and yes, make sure it's all in upper case even on Windows.

It may not exist on your system so bring up the Environment Variables window as shown earlier, and this time click the New... button. Specify the Variable Name as JAVA_HOME, and the Variable Value as the root folder (not the bin folder this time, but the one above that) that contains your JDK. So in my case it would be:

Code: Select all

C:\Program Files\Java\jdk1.8.0_51
No semi-colon, no final backslash. Click on the [OK] button to finish.

Bonus Point
For those who are unaware, one of the nice things about single path environment variables like JAVA_HOME is that once set they allow you to instantly navigate to that folder by typing %VariableName% (that is, the environment variable name surrounded by percentage signs) into the address bar of Windows Explorer. I've used this extensively over the years to set shortcuts to my server data folders, log folders and commonly used data file folders. If you have a folder which you need to access a few times per week and its faster to just type a 5 or 6 letter link in the address bar than to find it on your Windows Explorer favourites list, this is how you can set one up.

Why Don't I Have To Worry About This For The JRE?
Strictly speaking there is no need for you to worry about this, but I thought it was kinda cool so I'll explain it anyway.

In Java 8 (earlier versions used a less cool method) you will see that the path

Code: Select all

C:\ProgramData\Oracle\Java\javapath;
was added to your Path environment variable by the JRE install. Note that this is the ProgramData folder, not the ProgramFiles folder; quite different.

Inside that folder are three files, but not, as we have seen, the real JRE files; we know where they are. Instead they are symbolic links for the java.exe, javaw.exe and javaws.exe files. (I haven't mentioned the last of these, but it's Java Web Start. It allows you to download and run Java applications from the Web, and is not important to us at present.) Symbolic links are simply files which redirect to other files, which is what these three do; they link across to the corresponding JRE file.

Why is this cool? Because now it allows future upgrades to simply change the symbolic link files (or even add new ones) without ever needing to fudge around with modifying the Path environment variables again. As we've seen the paths for Java installs are version specific, so this way Java needs to only go to the (fixed) ProgramData folder to be pointed to the current JRE path. This is, in my humble, some good thinkin' and forward planning. (Yes, I still hate Java overall, but credit where it's due and all that.)

I'm not sure why the same approach wasn't taken with the JDK but I suspect it's because developers may be developing for multiple JRE versions at any one time, and therefore need to have a free hand in pointing to the environment that they want to use.
User avatar
Alan Kirk
Site Admin
Posts: 6606
Joined: Sun May 11, 2008 2:30 am
OLAP Product: TM1
Version: PA2.0.9.18 Classic NO PAW!
Excel Version: 2013 and Office 365
Location: Sydney, Australia
Contact:

Introduction To The Client Side Java API

Post by Alan Kirk »

Part 11 - The SSL Certificate

The absence of anything even approaching an introductory manual up until now means that this is going to be the biggest pain in your rectal region if you don't know about it. Unless you do this properly your code will fail to run and you will have no idea why, until you come across random posts on the Forum or random comments at the bottom of DeveloperWorks pages. Neither of those make a manual.

As you doubtless know, later versions of TM1 communicate using Secure Sockets Layer (SSL). This is not the place to be discussing that in detail, and most of the time we don't need to because aside from one or two certificate glitches when SSL was first introduced way back in 2000 and mumble (probably 2006), it has "just worked".

For the standard TM1 Excel client, the path to the certificate is specified in your Options.
000160_Cert01_800.jpg
000160_Cert01_800.jpg (133.81 KiB) Viewed 70348 times
Incidentally, .pem = Privacy Enhanced eMail, though it has nothing to do with e-mail any more. It is just a way of packaging / encoding SSL certificates.

With only that one exception way back when, I have never had to worry about that when using either the TM1 client or the classic VBA API. (Though within a couple of days after typing this Brian L identifies it as a potential problem with the C API as discussed on this link.)

Of course, what happens when this rapidly approaching date (10 years after the certificate start date, which is why I said "2006" above) ticks over is something I do not want to think about:
000170_Cert02.jpg
000170_Cert02.jpg (65.9 KiB) Viewed 70348 times
However you do have to worry about this with the Java API. A TM1 Admin Server is theoretically capable of listening on both SSL and non-SSL ports, but in reality current versions will be using SSL. And more to the point I don't recommend trying to bypass that security anyway.

Unless you import the Applix certificate into the JRE's certificate store (explained below), you are likely to be in for a whole mess of errors, not all of which will be obviously related to SSL certificate security. (Note well, not the JDK certificate store, the JRE one which is why I warned you about the extra JRE copy inside the JDK one back in Part 10.) Thankfully doing this is easy.

Understanding what you are doing and why with an almost complete lack of documentation is less so, so let's remedy that.

At the risk of flogging a dead horse, it's extremely important that you get this right, or the API code simply will not work.

Take a look at the folder structure of the JRE install below. It's divided into two folders, bin for binaries, and lib for libraries. Within lib we have a folder named security. Within that we have a binary file with no extension called cacerts (certificate authority certificates). This is the certificate store for the JRE.
000180_Cert03.jpg
000180_Cert03.jpg (118.71 KiB) Viewed 70351 times


Back in the bin folder we have another file called keytool.exe. This is the program that you use to manipulate the keystore.
000190_Cert04.jpg
000190_Cert04.jpg (149.31 KiB) Viewed 70351 times
And here's how you use it.
  • Open a Windows command prompt. Make sure that you right click on it and select Run As Administrator.
  • Use the cd command to navigate to the main jre folder. In my case that would be:

Code: Select all

cd C:\Program Files\Java\jre1.8.0_51
The reason for doing that is because we need to work with files which are under both of its sub-folders, so it's easier to go to the lowest common denominator.
  • Now enter the code shown below, changing the path to the applixca.pem file to match the one on your machine if necessary. (And yes, you do use a mix of forward and back slashes there; read on if you want to know why.)

Code: Select all

bin\keytool -import -alias aplx -keystore lib/security/cacerts -file "C:/Program Files/ibm/cognos/tm1_64/bin/ssl/applixca.pem"
You will probably be prompted for a password. Unless you've changed the password on your Java keystore the password will be changeit in lower case.

You will also be prompted about whether to trust the certificate. Type y and [Enter].

So what is this doing?
  • bin\keytool is obviously the path to the keytool.exe program. That's what you're running. Note that because I'm on a Windows machine there is a backslash between the folder and filename; I'm telling the command line to run that executable, so it uses standard Windows syntax.
  • The -import argument tells that program what you want to do; that is, import an SSL certificate. There are other commands available to delete or list the certificates.
  • The -alias argument is optional, but I highly recommend it. If you don't use it the certificate is likely to go in as something like mykey. I prefer to use an alias to simplify finding the thing in the keystore if I ever need to delete it for any reason, or to find it if I want to dump the keystore contents out to a file. Also the aplx expression is just what I chose to use; it has no special meaning beyond that, other than that I was pretty confident that the name wouldn't collide with any certificates that were already in there. You can call it anything you desire, but I've only tested it with all lower case alpha characters so if you stray from that path you do so at your own risk.
  • Now watch this bit carefully; keystore lib/security/cacerts is telling the keytool.exe application the path to your keystore file (that is, cacerts). But note that it's doing that using forward slashes, a fact that caused me some grief early on. Java is not a native Windows language; it was born in the fiery hell-pits of Unix where paths are designated by forward slashes. Even if you're doing this on a Windows machine as I am, you need to use the forward slashes for this argument for it to work. You'll come across this sort of thing in other parts of Java too when working with the file system, but I can't give you a complete guide to when it will happen. However if you are feeding a path to a Java command (which is what's happening here) rather than to Windows (which is what was happening with the call to the keytool.exe), the chances are that forward slashes will be the way to go.
  • -file is of course the path to the .pem file in which the Applix SSL certificate is embedded. Don't blindly copy mine; make sure that you specify your path here. And again note... forward slashes are used to specify this path.
I believe that you can use a parameter named -storepass if you don't want to be prompted for the password, but I've only tested that with deletions and listings.

If you ever do want to delete the certificate then call the same keytool application, but:
  • Use the -delete instead of the -import argument;
  • Use the -alias instead of the -file argument (hence the usefulness of using an alias).
  • In this one -storepass definitely does work if you don't want to be prompted for the password.
And if you can't remember what the alias is you can always list all of the imported certificates to find out. I recommend sending this to a text file then opening the file because it's apt to be a long, long list:

Code: Select all

bin\keytool -list -v -keystore lib/security/cacerts -storepass changeit > C:\Temp\AllCertsList.txt
(The -v argument is for "verbose output", which gives you all of the certificates' details.)

Again, this presupposes that you haven't (yet?) changed the password on your certificate store (and that you have a Temp folder under your C:\ drive). Again note that the path to the keystore uses forward slashes (since it's being fed to a Java command) and the output path uses backslashes (because that's a command to Windows). You'll get your head around it eventually.

That's it, the ugliest part of this is now over. (Except, of course, you need to do the same thing for the JRE of any machine that you're running the code on.)
User avatar
Alan Kirk
Site Admin
Posts: 6606
Joined: Sun May 11, 2008 2:30 am
OLAP Product: TM1
Version: PA2.0.9.18 Classic NO PAW!
Excel Version: 2013 and Office 365
Location: Sydney, Australia
Contact:

Introduction To The Client Side Java API

Post by Alan Kirk »

Part 12 - Installing Eclipse

Eclipse is an IDE for developing in Java and some other languages which is both free and exceptionally good. Granted I've seen people who are critical of its performance and stability for very large projects, but how much of that is really an Eclipse error and how much user issues is another matter. (How many times have we heard about new "bugs" in TM1 which nobody other than the one post wonder who raised them were ever able to find? And generally one who is a "consultant", but who doesn't know the difference between Perspectives and a server.)

There was a time, not very long ago, when Eclipse was the "do everything" IDE for Java. You wanna write SE code? No problem. EE code? No problem. Android code? No problem, there's a plugin to generate that.

Then Google screwed it over by pulling support for that plugin, and trying to strong-arm Android developers into Android Studio. Which I suppose doesn't matter if your life is Android, but does matter if you need to work on other Java projects or, heaven forbid, languages like PHP which Eclipse supports but Android Studio being for, well, Android, doesn't.

Eclipse's other primary weakness is in code-folding; that is, where you can collapse a block of code to de-clutter your display once you've finished writing the block. If you've used Notepad++ with the TM1 language added in you'll know that you can collapse (for example) while blocks so that you can focus on the code around that. Eclipse for whatever reason doesn't easily or cleanly fold blocks smaller than a method. (There are some workarounds, but they're really awful hacks.) So when you're done writing your method you can collapse it to hide it by clicking on a toggle in the left hand gutter of the code pane so that you can turn your attention elsewhere, but you can't (easily or intuitively) collapse a block smaller than that.

Accordingly before you decide to throw your lot in with Eclipse, I thought I should mention those caveats.

There are still multi-flavour IDEs out there which do support Android in addition to conventional Java, such as Jetbrains' Intellij IDEA. This is a commercial product, though for individual users the price isn't excessive given what it does. (At the time of writing, $US199 for individuals (though there's a licence restriction that it not be used in corporate work) or $499 for corporate users, which would of course be tax deductible. (In Australia at least, and I expect in most jurisdictions.)

There is a free Community edition of Intellij IDEA, which will do SE code... but not EE code.

However since Android is less important to me than the combination of SE and EE, I'm staying with Eclipse for now. You can download it from https://eclipse.org/downloads/

Version Selection And Download
000200_EclipseDownload_900.jpg
000200_EclipseDownload_900.jpg (96.74 KiB) Viewed 70346 times
There are multiple versions for different operating systems, both 32 bit and 64 bit, so choose the one that applies to you. You'll also notice that there's a version for EE developers and an Eclipse IDE for Java Developers one which is targeted at SE developers. According to the Eclipse comparison chart the only thing that EE lacks compared to the SE edition is the WindowBuilder core. Naturally EE contains a great many server-oriented tools that the SE one doesn't.

Given that I know that I'll need to do work in EE at some point and I've already said what I think about Java GUIs, I decided to go for the EE one. You should be fine with either.

Eclipse uses a very astronomical naming system. Although each version does actually have a number it's usually referred to by name. Up to the time of writing the versions were:
  • 3.3 Europa
  • 3.4 Ganymede
  • 3.5 Galileo
  • 3.6 Helios
  • 3.7 Indigo
  • 4.2 Juno
  • 4.3 Kepler
  • 4.4 Luna
  • 4.5 Mars
Traditionally Eclipse has come as a zip archive that you simply unpack to extract the "Eclipse" folder contained within, and (ideally) move that folder to your Program Files folder in Windows. (Which, of course, you need admin rights to do, but if you don't have those you're going to find development in Java or any other language problematic at best.) Putting it in Program Files is optional; it can really be run from anywhere, but I would recommend that you use the Program Files folder for housekeeping purposes if no other; it's a lot easier to find something if it's in the location that was designed for it.

Create a shortcut to the application so that you can run it from your desktop or start menu.

As the right hand side of the screenshot above suggests, Eclipse Mars (4.5) version (June 2015 onwards) contains a "real" installer which can be used as an alternative to the method above, though the .zip file download is still available for the traditionalists.

Note: There is a remote chance that you will be asked to specify the path to the JRE the first time you start Eclipse. If so, follow the instructions shown under the heading "Preferences - Path To The Compiler" later in this section.

The Workspace Folder
When you start Eclipse it will ask you for a workspace directory. This is where you keep your Eclipse projects and by default it will probably be a folder called workspace in your user folder. Change this if you want.

I keep mine on my OneDrive / SkyDrive folder so that I can move between machines easily (I use four in day to day use, so...), and so that all of my work is backed up to the cloud. Of course that only works because obviously confidentiality isn't an issue for what I'm doing in Java. Use whatever is convenient for you.

Also, it's possible to have multiple workspace folders and choose the one that you want to work in at startup. I won't be demonstrating that, but if you had such a list you would be able to select it from the dropdown shown below.
000220_Eclipse_Workspace.jpg
000220_Eclipse_Workspace.jpg (46.48 KiB) Viewed 70351 times
The first time at bat you won't have any paths specified, so browse to or enter the path that you want to use. If it's always going to be the same one, check the "Use this as default" box so that you don't see this dialog in the future.

You can restore that setting by going to Window => Preferences => General => Startup and Shutdown => Workspaces.

Watch out for one thing; with some versions (Mars in particular) the workspace needs to be updated which will render it incompatible with older versions. When you upgrade Eclipse to a newer version you should do it to the same version on every computer that you work on.

You will typically see the Welcome screen on your first startup.
000210_Eclipse_Welcome.jpg
000210_Eclipse_Welcome.jpg (66.95 KiB) Viewed 70351 times
I don't find this particularly useful, so I just jump to the Workbench using the link at the top right. (Alternatively you can select one of the arrangements of panes (called "perspectives", no, not that Perspectives) at the top left. Eventually the Welcome screen stops appearing, but if you ever want to see it again you can get to it by Help => Welcome.

The tabbed windows on the Eclipse workbench are called "views". If you're used to working with Adobe products like Photoshop these would be known as panels, but "views" is the Eclipse name. An arrangement of the panels is called a perspective. Different perspectives exist for code editing and debugging for example, and as with most IDEs you can create and save your own arrangements.
000230_Eclipse_Workspace_900.jpg
000230_Eclipse_Workspace_900.jpg (167.56 KiB) Viewed 70348 times
The Java perspective typically has the following views:
  • On the left, Project Explorer. This shows all of the projects that you have created or imported into the Eclipse environment. It is not necessarily every project that you have in your workspace folder, nor do the projects which are shown here need to be in your workspace folder. Projects can be imported into and removed from the Project Explorer independently of their existence in the file system.
  • In the centre, the main code editing window which needs no explanation.
  • Below that, a number of tabbed windows including Problems (which has a list of any compile errors, unused variable warnings and so on), Javadoc which lets you see the documentation (if any exists and is linked properly) for methods and fields, and others including the Console window to which output will be sent.
    • Incidentally, each time you save your files Eclipse does a background compile to identify any compilation errors without you needing to do a full compile. These errors will appear in the Problems window as mentioned above. But equally if you fix some problems, they may not leave that window immediately. Save the files (using the Save icons on the right hand side of the main toolbar) to do a save and update the values.
  • On the right, Mylyn is a task management plug-in, which we won't be looking at.
  • Below that is an Outline view which provides you with an outline of the current code (if any is loaded), allowing you to quickly navigate to field and method definitions.
The equivalent to Intellisense in Eclipse is to press [Ctrl]+[Space], which you should definitely learn as it will pay huge dividends.

Setting Preferences
We've already seen one of the preference settings (for the Workspace), but the full set can be accessed from Preferences at the bottom of the Window menu. The full range of configuration options for Eclipse is overwhelming, but for the most part you need only a handful of them.
000240_Eclipse_Preferences01_900.jpg
000240_Eclipse_Preferences01_900.jpg (160.78 KiB) Viewed 70346 times
Note that there is a General set that applies to all projects, and a more specific set that applies to the various languages and modules that Eclipse deals with.

If you want to change the size of or colour of your font, you can do that through the General twirl-open. The generic font and size is set in the Basic group, which I haven't opened in the example below. The Java set defines colours and formats which are specific to Java language elements, just as there are other twirl-opens that relate to other languages. As you can see at the top of the box, you can search for the elements that you want to format. I would suggest leaving that alone unless you really need to.
000250_Eclipse_Preferences02_800.jpg
000250_Eclipse_Preferences02_800.jpg (200.36 KiB) Viewed 70346 times
However there is one thing that I suggest you change in the Java section of the left hand pane. (Not the General section illustrated above, the Java one illustrated below).

Click on the Editor twirl-open. (Don't twirl it open, just click on it.)

Change the Matching Brackets Highlight colour to bright red as shown. By default it will be some kind of hard to see grey. My preference is also to change the bracket highlighting radio button to Enclosing Brackets as shown.
000260_Eclipse_Preferences03.jpg
000260_Eclipse_Preferences03.jpg (218.93 KiB) Viewed 70351 times
What's the effect of that? When your cursor is inside a set of braces and both of them are visible on screen, both will be lit up with a nice, red box. It helps with being able to match them off, especially if you've copied code from another location and you missed a closing bracket:
000270_Eclipse_Preferences04.jpg
000270_Eclipse_Preferences04.jpg (83.18 KiB) Viewed 70351 times


Preferences - Path To The Compiler
There is one important preference that you hopefully won't need to worry about, but just in case...

If for some reason Eclipse couldn't find the path to the JRE, you need to specify it so that it can do the background compilation. (And running the code when you test it.)
000280_Eclipse_Preferences05_900.jpg
000280_Eclipse_Preferences05_900.jpg (134.94 KiB) Viewed 70346 times
To do that, go to Java => Installed JREs. If there are none defined, click on Add... and follow the prompts. (Name is obvious, Location is obvious, and for our purposes you'll always select Standard VM.)
User avatar
Alan Kirk
Site Admin
Posts: 6606
Joined: Sun May 11, 2008 2:30 am
OLAP Product: TM1
Version: PA2.0.9.18 Classic NO PAW!
Excel Version: 2013 and Office 365
Location: Sydney, Australia
Contact:

Introduction To The Client Side Java API

Post by Alan Kirk »

Part 13 - Create A New Project

Obviously the starting point is to create a new project.

In Eclipse, right click anywhere in the Project Explorer view and select New => Project.
000285_NewProject.jpg
000285_NewProject.jpg (56.84 KiB) Viewed 70351 times
Depending on the version of Eclipse you're using you may be prompted to choose a project wizard. If so, find the Java Project option from the list and choose it. You should then be presented with a dialog like this (or, if there was no Wizard selection, you may go straight there):
000290_NewProject.jpg
000290_NewProject.jpg (131.65 KiB) Viewed 70351 times
Generally the only thing that you need to enter is the project name.

The Use Default Location checkbox means that the project will be in a new (automatically created) folder under the path that you set as your workspace directory the first time you started Eclipse. You can uncheck that if you want to save it somewhere else.

The JRE allows you to target the code to a specific version of the JRE (which you would need to have installed). If you set this to say 1.7 but used language features that were added in 1.8 then I imagine that you would get compile errors, though I admit that I haven't tested that.

Generally if you're writing code for your own use you should just go with the default JRE and make sure that that version or higher is installed on all of the machines that you use.

The Project Layout allows you to specify whether:
  • You will have your .java plain text source code and .class compiled bytecode files somewhere below the project's root folder; or
  • Whether the .java files go into a folder named src and the .class ones into a folder named bin.
There is no good reason for dumping everything in the one folder, unless you're the kind of person who likes throwing everything into your wardrobe and having an adventure trying to find it again.

Working sets are an Eclipse feature that allow you to group projects and other resources into groups when you start to have long lists of them. We won't go into them further here. At the bottom of the dialog there is a [Next] button which will take you to a further dialog that you really don't need to do anything on when you're still getting up to speed with Java, but should probably take a peek at for curiosity's sake:
000295_NewProject.jpg
000295_NewProject.jpg (138.98 KiB) Viewed 70351 times
You could, for example, include new source code folders, or change the folders that your .class files will be output to. Or you could add references to other .jar libraries including external ones (that is, ones which aren't part of your project folder) on the Libraries tab. But I'm not going to do that, and we'll see why shortly. Aside from which any of these things can also be easily done from inside Eclipse after you have started coding. You don't need to do it here.

Click [Done] to create the project.
User avatar
Alan Kirk
Site Admin
Posts: 6606
Joined: Sun May 11, 2008 2:30 am
OLAP Product: TM1
Version: PA2.0.9.18 Classic NO PAW!
Excel Version: 2013 and Office 365
Location: Sydney, Australia
Contact:

Introduction To The Client Side Java API

Post by Alan Kirk »

Part 14 - The First Class

The content of the new project is pretty sparse. The Project Explorer shows only a single folder called src and a reference to the core Java libraries, named here as the JRE System Library. Note that it has jre1.8.0_51 next to it indicating that it relates to that version. Had I created the project to point to version 1.7, it would have had that version instead.
000300_NewProject_900.jpg
000300_NewProject_900.jpg (128.07 KiB) Viewed 70346 times
The src folder is where all of your source code will go, as you would expect. We'll create some in a moment.

As I mentioned earlier, Java code is compiled to bytecode (binary) .class files which are packaged into .jar (Java ARchive ) files which are Zip format archives with a different extension.

In fact the core libraries contain other types of files as well, but as Java novitiates looking at those is way beyond our current paygrade. Strictly speaking we never need to drill down into any of the files in there; instead we're far better off using the Java documentation to find the classes that we need. However just out of curiosity:
000310_NewProject.jpg
000310_NewProject.jpg (120.47 KiB) Viewed 70351 times


Looking at the rt.jar archive, we find that it contains packages. As I discussed oh so very long ago now, packages are in fact folder structures and we'll create our first one in just a moment. One of the packages, originally written by Sun Microsystems and therefore using com.sun as its reverse domain name notation system, is the com.sun.beans one. And that contains three classes which... actually I have no idea what those ones do and care even less just at the moment. The only thing I wanted to illustrate is "that's what the system libraries are".

OK, so let's create a class of our own, and a package to put it in.

We'll be working for the mythical yourcompany.com so the start of our package name will be com.yourcompany. After that we could go for the name of the project followed by a name indicating what the contained classes is, or we could skip the project name and go straight to the package purpose.

Yeah, let's do that, my fingers hurt. We'll call it mainpackage (all lower case for package names, remember).

Our class will be called MainCode. It's a class so we use mixed case, always starting with a capital letter.

Creating it is as easy as right clicking on the project entry in Project Explorer and selecting New => Class
000320_NewClass01.jpg
000320_NewClass01.jpg (118.94 KiB) Viewed 70351 times
That launches the New Class dialog:
000330_NewClass02.jpg
000330_NewClass02.jpg (140.3 KiB) Viewed 70351 times
The default source folder is the src folder that we saw above inside your project.

The Package, you can in fact leave blank in which case the class will be put directly under the src fdolder. That's called the default package, and putting your code there is a very bad idea. Eclipse will warn you about this. Not in a "are you crazy, I'm not compiling that!" kind of way, but there will be a warning at the top.

OK, it looks like this if you must know:
000340_NewClass03.jpg
000340_NewClass03.jpg (46.92 KiB) Viewed 70351 times
So to avoid that, we are putting it into a package as shown earlier.

Disregard the Enclosing type checkbox, that's way, way down the advanced coding track.

The name of the class is obvious. As this will be our starting class, it has to be public, so that's fine. Abstract and final are also further down the Java learning path than I'll be taking you.

The Superclass should be obvious if you stayed awake through the section on Inheritance. This class will inherit methods and properties only from the absolutely top level class.

Interfaces are down the path alongside abstract and final. You don't need them yet.

One thing that I did change was to check the public static void main checkbox. You do that for your very first class and your very first class only. As I have mentioned repeatedly, the main() function is the starting point for a console application. It's where the code starts running. You need to have one, and only one. And since this is the only class we'll have for the moment, it needs to have a main function. You can create it manually of course, but there's no reason not to let Eclipse do it.

The other items can be left with their defaults. When you click on [Finish], this is what we see:
000350_NewProjectPlusClass_800.jpg
000350_NewProjectPlusClass_800.jpg (136.92 KiB) Viewed 70346 times
Over on the left you will see Project Explorer showing you that there is a new package, under which is a new .java file called MainCode. That's our new class.

On the right you can see that the new class appears in the Outline. All of its properties and methods appear under that (there's only one at the moment), and you can navigate to them by just clicking on them.

In the centre, we have the code for the new class in the main window.

Tip: If you double click on the code's tab, it will expand to fill the screen. Double clicking again resizes it back to the original size.

Note the declaration of the package at the top. This is required for all classes, though as long as you create it via the Eclipse interface it will take care of that for you.

Below that we have the class name, and the opening brace (the closing one for which is on line 10) defining the code block of the class.

Definition Of A Method
Within that we have one method defined:
  • public, meaning that it's visible to the whole project (as it has to be, since if you can't see the main() method you won't get very far running the thing).
  • static, meaning that you don't need to create an instance of MainCode before you call this method (which, again, is pretty much essential because there is no code to create an instance of MainCode before main() itself runs).
  • void, meaning that this method returns no value. (Because, what would it be returning the value to? Console applications can return completion codes to the operating system that called them, but not "real" values the way that methods within the project can.)
  • main, with a lower case m as required for methods, because the Java system requires that the starting method be called main.
The method has one argument; a string array of as yet undefined length called args. The presence of the string array is mandatory. The use of the name args is not.

That's followed by the opening brace for the method's block of code (the closing one being on line 8).

And within that there is nothing but a comment (// being the code for an inline comment in Java) telling you that although Eclipse has been nice enough to create the method for you, it has not the first idea what you want to do with it so maybe it would be a good idea if you wrote some code?

Tasks And TODO Items
Incidentally, when you write a comment in that format (with TODO immediately after the double slashes) you can open a view (panel) called Tasks which will show you every entry in the project which has TODO comments, like so:
000360_Tasks_900.jpg
000360_Tasks_900.jpg (210.02 KiB) Viewed 70346 times
Project Explorer Vs The File Structure
The last thing to be aware of here is that if you're relying on Project Explorer to show you what's happening on your disk system, then I'm afraid I have to tell you... it's lying to you. Not in an Ashley Madison way; it actually has your best interests at heart by keeping the view decluttered and restricted to content that affects your source code. Thus it shows packages as a single "thing" and doesn't show you the compiled .class files at all.

If you want to see that, open the Navigator view from the Window => Show View menu item. You'll see this:
000370_Navigator.jpg
000370_Navigator.jpg (43.97 KiB) Viewed 70351 times

This shows what's really happening under your project folder on your file system, the same thing that you'd see if you looked in Windows Explorer. There is now a bin folder (not shown in Project Explorer because it isn't relevant to the creation of source code), under which there is a separate folder for each name between the periods in your package name.

Because we elected to have source and compiled code separated when we first created the project (see that dialog earlier for details), that package tree structure is replicated under both the bin and src folders.

There are two system files, one named classpath (which is used to tell the JRE where to look for compiled classes when it runs), and one named project, which contains information about the project for Eclipse's reference.
User avatar
Alan Kirk
Site Admin
Posts: 6606
Joined: Sun May 11, 2008 2:30 am
OLAP Product: TM1
Version: PA2.0.9.18 Classic NO PAW!
Excel Version: 2013 and Office 365
Location: Sydney, Australia
Contact:

Introduction To The Client Side Java API

Post by Alan Kirk »

Part 15 - Hello World

The traditional first program in any language is the Hello World one where those two words are output to the screen.

I'm going to work on the assumption that most readers, even the ones who have never used Java before in their lives, are a bit over that, so let's kill a few birds with one stone.

The args Array
I have periodically been hammering on about the args string array and with good reason; it's the way that we communicate with the program. In our example we need to call the application from a TI process, which we do with ExecuteCommand. But we also need to pass in a list of the stocks that we want read.

How do we do that? We specify them as a space delimited list of arguments after the name of the .jar package. Those are fed into the args string array, from which they can be processed.

So let's look at how we can do that, and while we're at it how to iterate through an array, how to output to the console (hey, you know we gotta do it, right?) and how to test run the application simulating the passing in of arguments to the args array.

Iterating The Array, For Loops And Eclipse Templates
Iterating the array is easy. We want to do a for loop. All I need to do is type the word for and then press [Ctrl]+[Space]. As I mentioned earlier, that is arguably the single most important keystroke you can learn in Eclipse.

It launches a number of templates relating to the word "for":
000380_EclipseTemplate_900.jpg
000380_EclipseTemplate_900.jpg (103.91 KiB) Viewed 70346 times
The first suggestion is "iterate over array", the skeleton for which is shown in the pop-up to the right so that you can get an idea of what you'll be getting if you select it.

The conditions for a for loop are, as is the case with many other keywords, enclosed in round brackets. There are three arguments in this syntax:
  • The first defining a counter variable and setting its starting value. Note that this is int i (which declares a variable i with a data type of integer), followed by =0 (which initialises its value). There are two things to be aware of here. The first is that because i is initialised within the for loop, it only exists inside that for loop. If for whatever reason you have to use it outside of the for loop, then declare it outside and simply initialise it to zero in the first argument to the for loop. The second is that as I have previously mentioned, arrays start at zero in Java which is why the code template sets it to that value.
  • The second stating how long the loop will run for. As I mentioned, arrays in Java are complex objects and therefore have methods. .length is one of them; it defines how many elements the array has. Note that the condition only runs while i is less than that length, again because arrays start at base zero. Eclipse is smart enough to know that it needs to loop through args because it's the only array in sight.
  • The third stating how to handle the counter each loop. In this case it uses the increment operator that I mentioned previously to increment the counter by 1 each time (equivalent to i = i+1)
This is followed by opening and closing braces defining a code block.

One more time, whether it's an if, a while, a for, a try, a catch, a finally, a method, a class... everything after the definition will be a code block, and must therefore sit inside braces.

So all we need do is hit [Enter] and the code will be inserted for us.

Now we need to put some code in to actually do something. The equivalent of the old and faithful debug.print in VBA is

Code: Select all

System.out.println();
That is, from the System class that is part of the built in libraries, get a property named .out. .out returns another object named PrintStream. For that object, call a method called .println which outputs the text that you pass to it between the brackets followed by a newline character.

Eclipse should have a predefined template set up for this as well, in which you can type sysout followed by [Ctrl]+[Space] to have it auto-created. Since sysout is your bestest debugging buddy, that one is worth learning as well.

So let's say that I write it this way:
000390_ForLoop.jpg
000390_ForLoop.jpg (100.21 KiB) Viewed 70351 times
Things to note about the above:
  • With my cursor on row 10 you can see how the braces are highlighted after I set up the Eclipse preferences. It matters less for small blocks like these but with large, nested blocks that can really help you find your way around.
  • Note how I split the println statement over two lines. With the semi-colon required as a line end terminator it's easier to do this in Java than in VBA.
  • As I mentioned before, the + operator is used to join strings as well as for addition.
  • The current element of the args array is accessed using square, not round brackets. That takes some getting used to if you're from VBA.
Running The Application
Right, we're all set to go! To run the thing we go for this green play button up here on the toolbar,
000400_Run.jpg
000400_Run.jpg (30 KiB) Viewed 70351 times
so let's hit that, and...

Absolutely nothing happens. Why, whyyyyy?????

Because we haven't faked any arguments yet, that's why.

Instead of clicking on the button, click on the drop down to the right of the button and chose Run Configurations.
000410_RunConfig1.jpg
000410_RunConfig1.jpg (72.63 KiB) Viewed 70351 times
The first thing we'll do is give the run configuration a name; I'll call it RunWithFourStocks:
000420_RunConfig2.jpg
000420_RunConfig2.jpg (172.8 KiB) Viewed 70351 times
Note that if you already have a run configuration and change it here, then you are just changing that configuration, not creating a new one. There is no Save As button. If you want to create a new one, click on that first button way up at the top left first.

The astute reader will note that there are multiple things that you can set here including even a different JRE to test compatibility. But the only thing we're interested in is the variables, so I'll select that tab and add in four stock codes; BHP, Tassal, BP in London, and Boeing.

It seems that you can enter them either on new lines, or as a comma delimited list; either way worked for me:
000430_RunConfig3.jpg
000430_RunConfig3.jpg (85.34 KiB) Viewed 70351 times
When I run that configuration:
000440_RunConfig4.jpg
000440_RunConfig4.jpg (318.91 KiB) Viewed 70351 times
We have output, and if you've been following along we now have our first functional Java code. But let's not forget what our code does, or get around to documenting it "someday".
User avatar
Alan Kirk
Site Admin
Posts: 6606
Joined: Sun May 11, 2008 2:30 am
OLAP Product: TM1
Version: PA2.0.9.18 Classic NO PAW!
Excel Version: 2013 and Office 365
Location: Sydney, Australia
Contact:

Introduction To The Client Side Java API

Post by Alan Kirk »

Part 16 - A Running Commentary

Documentation is the bane of a coder's life. The fun part is in getting the machine to "do stuff". Until you have to modify the code 6 months later and can't for the life of you remember what the stuff was supposed to do, much less how it works.

I've added a second procedure to our code:

Code: Select all

package com.yourcompany.mainpackage;

public class MainCode {

	public static void main(String[] args) {
		for (int i = 0; i < args.length; i++) {
			System.out.println(
			    "Get the value for " + args[i]);
			System.out.println(getLength(args[i]));
		}		
	}

	public static int getLength(String str){
		//This is a single line comment.
		//It can be turned on and off with [Ctrl]+[F7]	
		return str.length();		
	}
}

As I mentioned before, the equivalent of a single quote to comment out a line is the double forward slash. You can also put these at the end of a set of braces to indicate what code block the brace is terminating, something that can come in very handy with deeply nested code blocks to ensure that a line of code slots in where it's supposed to.

With Eclipse you can select multiple of lines of code and hit [Ctrl]+[F7] to comment or uncomment them in one hit.

But what about methods?

There is a second commenting syntax which can be effected by typing /** (forward slash double asterisk) on a blank line, just above (say) a method definition. As soon as you press [Enter] a code template is created, ready to be filled in like so (note the way it automatically reads the parameters / arguments and adds them with @param tag(s)):

Code: Select all

	/**
	 * 
	 * @param str
	 * @return
	 */
	public static int getLength(String str){
		//This is a single line comment.
		//It can be turned on and off with [Ctrl]+[F7]	
		return str.length();		
	}
Now if I add the details of the method to the first line and some information about the parameters and return value:

Code: Select all

	/**
	 * Gets the length of a string. Very ingenious code, hand
	 * crafted by Wyle E.Coyote. 
	 * @param str The length of the sting that you want to get.
	 * @return An integer representing the return value of the string.
	 */
Note that I have added an extra line above the @param entry; Eclipse automatically added the asterisk when I pressed [Enter] as it knew that it was in a comments block.

So what's the advantage of doing this?

First, using standard blocks like these can help you generate standard format Javadocs so that you can have a reference for your classes, methods, fields, etc. This is something that we won't be going into.

Second, when you're coding you get this when you call a documented member:
000450_Help_800.jpg
000450_Help_800.jpg (130.67 KiB) Viewed 70339 times
(Note that the split lines in the source comment have been joined back together again. You therefore don't need to worry about positioning the splits "correctly" if you need to split comments about the procedure up over several lines to keep them readable in the editor.)

Not a huge help when the procedure is right below it and written in the same 5 minutes as it is here; more of a help when the method was written 8 months ago and is in some other class buried deep in a package that you haven't needed to look at for 7 of those months.

Next up... time to load some libraries and hook up with the TM1 API
User avatar
Alan Kirk
Site Admin
Posts: 6606
Joined: Sun May 11, 2008 2:30 am
OLAP Product: TM1
Version: PA2.0.9.18 Classic NO PAW!
Excel Version: 2013 and Office 365
Location: Sydney, Australia
Contact:

Introduction To The Client Side Java API

Post by Alan Kirk »

Part 17 - Adding New Java Libraries

As we know, Java comes with a whole raft of prebuilt system libraries containing probably thousands upon thousands of class methods to help you do an immense number of programming tasks right out of the box.

Yet sometimes it isn't enough, and we need to turn to third party libraries.

In my case I need two. One is obviously the TM1 Java API .jar library.

The second is a library to wrap around the Yahoo Finance API, and this one illustrates an interesting point with Java development. I don't in fact need the Yahoo Finance library at all. Java's core libraries contain everything I need to make a call to the raw Yahoo Finance API. However many of Java's functions are very low level and you need to bolt a lot of them together to get the result that you want. And if someone has already done that, as is the case with Yahoo Finance, there is no need at all to do it yourself. Instead you just import that library, and call its classes and methods.

A good source of such prebuilt code is the Apache Commons (https://commons.apache.org/), which is worth checking out.

So where do we put that code?

The lib Folder
If we want to incorporate it directly into our project (which I will be for both of those) the traditional place to put it is in a folder called lib for libraries. In Eclipse simply right click on the project and select New => Folder and name it lib like so:
000460_Folder_900.jpg
000460_Folder_900.jpg (132.71 KiB) Viewed 70279 times
The dialog does allow you to specify some advanced options like whether it is a virtual folder, but we won't be using those.
000470_FolderDialog_700.jpg
000470_FolderDialog_700.jpg (68.46 KiB) Viewed 70279 times
The doc Folder
At the same time as I create a lib folder I create a doc folder. Those folders are commonly found in Java projects but I'm not sure whether they're meant to hold a project's own documentation exclusively, or imported documentation as well. (I can't find a definitive answer on this one way or the other.) However that's how I'm going to use it.

Downloading The Yahoo Finance Library
This is optional but you won't be able to work along with me if you don't do it. The Yahoo Finance API library can be downloaded from this location:
http://financequotes-api.com/

The download is a conventional .zip file which contains both the various .jar versions (I'm using the 2.0 one only) and a folder containing the Javadoc files for documentation.

Crack open the zip file, extract the 2.0 .jar, and copy it to your new lib folder. (You can copy the .jar file in Windows Explorer and paste it directly into the lib folder inside Eclipse but you have to extract it from the .zip archive first. You can't paste straight from a .zip archive into Eclipse.) There's no point having it stored outside of the project's lib folder since it's not likely to be used with any other projects.

Now rename the doc folder that you extracted from the .zip archive as doc_Yahoo. (Or something that makes it more unique than just "doc".) Copy that into your doc folder in Eclipse. We'll use that later.

Getting The TM1 API Library
One thing to be aware of; it would be possible for you to set a reference from Eclipse to the existing location of the TM1 API .jar. You will see, for example, that there are references to "External Jars" that can be set.

I'm not going to do that, I'm going to copy the thing into the lib folder of my project. Why? Because I don't trust IBM not to make changes to the .jar file in an update package and if the application suddenly stops working for any reason, I want to be sure that the API library changing under my feet isn't that reason. That having been said, if you do a server upgrade, make sure that you don't hit a versioning issue with the API library. You probably won't; I developed the example across both 9.5.2 and 10.2.2 and the only time I had a problem was when I was using the 10.2.2 .jar to try to connect to 9.5.2 by accident. The other way around seemed OK.

Needing to do that check is a disadvantage of loading the library into your project, but it's a disadvantage that I'm prepared to wear to make sure I know what changes and when.

In a standard 10.2.2 installation the .jar will be found here:

Code: Select all

C:\Program Files\ibm\cognos\tm1_64\bin\classes\TM1JavaApi.jar
Of course, whether that will still be true after 10.2.2, who knows. If you're on any other version, or installed to a non-standard location, just do a search for a .jar file of that name.

Copy the .jar into your lib folder. Again you can paste the thing straight into Eclipse.

The documents will be here:

Code: Select all

C:\Program Files\ibm\cognos\tm1_64\TM1JavaApiDocs
Copy that folder (the entire folder, not just its contents) into your doc folder.

You should end up with something like this:
000480_LibsImported.jpg
000480_LibsImported.jpg (174.45 KiB) Viewed 70279 times
None of this does us any good yet, though, because we still have to hook the libraries up to the project. So let's do that.
User avatar
Alan Kirk
Site Admin
Posts: 6606
Joined: Sun May 11, 2008 2:30 am
OLAP Product: TM1
Version: PA2.0.9.18 Classic NO PAW!
Excel Version: 2013 and Office 365
Location: Sydney, Australia
Contact:

Introduction To The Client Side Java API

Post by Alan Kirk »

Part 18 - The Build Path And JavaDocs References

The first thing is that we need to tell the compiler (via Eclipse) that we need these libraries included when we build the project. And in Eclipse this could not be simpler.

For each of the libraries, right click and select Build Path => Add To Build Path
000490_AddBuildPath.jpg
000490_AddBuildPath.jpg (159.36 KiB) Viewed 70278 times
If you've done this correctly you will now see the libraries up alongside the JRE System Library like so:
000500_BuildPathsAdded.jpg
000500_BuildPathsAdded.jpg (135.61 KiB) Viewed 70278 times
Now that is in fact enough to make them work. But we will still be missing one nicety; remember that helpful little tag that popped up over the Wyle E Coyote process a couple of parts back? We won't get those, because the libraries have no idea where their documentation is. Instead we get this:
000510_NoJavaDocHelp.jpg
000510_NoJavaDocHelp.jpg (66.31 KiB) Viewed 70278 times
Thankfully that's an easy fix as well. Right click on the library in question (either the one in the lib folder or the one up with the System libraries, it doesn't matter which) and select Build Path => Configure Build Path.
000520_ConfigureBuildPath01.jpg
000520_ConfigureBuildPath01.jpg (130.36 KiB) Viewed 70278 times
Go to the Libraries tab and select Javadoc location. You can either double click on it or single click and select Edit.
000530_ConfigureBuildPath02.jpg
000530_ConfigureBuildPath02.jpg (73.41 KiB) Viewed 70278 times
Now unless you're feeling really confident about your Java file system syntax, I do suggest that you use the [Browse] button. You can paste the path to the Javadocs (which we have in our Docs folder) into the Folder field of that, but don't try to do it into the Javadoc Location Path because it needs to be filled with forward slashes and such as you'll see from the screenshot after the next one.
000540_ConfigureBuildPath03.jpg
000540_ConfigureBuildPath03.jpg (135.55 KiB) Viewed 70278 times
Once you've selected the path, click on the [Validate] button. That doesn't guarantee that you've hooked the help files up correctly, but it checks for the presence of expected files which are essential for a valid Javadoc pachage:
000550_ConfigureBuildPath04Validate.jpg
000550_ConfigureBuildPath04Validate.jpg (95.11 KiB) Viewed 70275 times
If all is well, check out the difference:
000560_ContextSensitiveHelp.jpg
000560_ContextSensitiveHelp.jpg (128.52 KiB) Viewed 70278 times
As the bottom right of the pop-up states, you can press [F2] to jump into the dialog so that you can scroll through its whole content.

Now we're ready to write some code using those libraries.

Loading The Java Docs In Your Browser
But just before we do, you should also be aware that you can open the JavaDocs library in your browser, which can make finding methods and fields easier.

In a default 10.2.2 installation you should find the home page at:

Code: Select all

C:\Program Files\ibm\cognos\tm1_64\TM1JavaApiDocs\index.html
I would recommend navigating to the folder specified and double clicking on the index.hrml file to launch it into your browser, then bookmarking it (adding it to favourites, whatever your browser does) so that you can easily get back to it.
000565_JavaDocHTMLHelp.jpg
000565_JavaDocHTMLHelp.jpg (88.78 KiB) Viewed 70275 times
User avatar
Alan Kirk
Site Admin
Posts: 6606
Joined: Sun May 11, 2008 2:30 am
OLAP Product: TM1
Version: PA2.0.9.18 Classic NO PAW!
Excel Version: 2013 and Office 365
Location: Sydney, Australia
Contact:

Introduction To The Client Side Java API

Post by Alan Kirk »

Part 19 - Introduction To The Example Project

I'll now switch across to the actual project that I built.

I'll repeat a warning that I gave earlier; I approached the design of this as a teaching tool more than as a production application so there are examples of what not to do in here as well.

The Packages And Classes
The application is broken into four packages:
000570_ExampleProject01.jpg
000570_ExampleProject01.jpg (74.53 KiB) Viewed 70278 times
They are:
  • logwriter contains a single class (LogWriter) whose purpose is to output any error messages to the server's log directory.
  • mainpackage contains the MainCode module, which contains the main() procedure plus some private (class module level) variables, and nothing else.
  • stockreader contains the single class StockReader.
  • tm1writer contains three classes:
    • TM1Connector, the methods of which are called to create a connection to a server,
    • TM1StockCubeCreator which creates the stock cube if it doesn't exist;
    • TM1StockWriter, which takes the data that was obtained by the StockReader class and writes imert to the cube.
Now let's look at that from a production system perspective, bearing in mind that the decision of what to make a class and what to put in a package is as much art as science.

Should LogWriter have its own class and package? Probably yes, because if it was a serious attempt to create an easy to call log file generator it may be reusable in other projects. (Though if you looked hard enough there's probably already a class out there that will do this and more. But let's suppose that it had some functionality that was specific to our business.) Indeed it could be packaged up as part of a .jar library of our own at some point. Of course this particular LogWriter is nowhere near that good but that's only because it's a demonstrator.

What about StockReader? Maybe. The primary method of this takes a list of stocks and returns it as a collection of Stock objects. (Stock classes being defined in the Yahoo library.) It's quite possible that we could have a need to use this to populate other databases, so there might be some justification for that if we need to reuse it.

That having been said, Eclipse supports a feature called Refactoring which allows you to rename objects, shift them to another package, split code from one procedure into another procedure and so on. We won't be looking at that in this document but it does make it very easy to split code that was originally thought to be "single use" into a library.

TM1Connector? Probably not. The code is trivial and only exists so that I can break out methods to show you the way to connect to TM1 in detail, and in isolation. The methods could just as easily be embedded in the MainCode class.

TM1StockCubeCreator? A bit of a coin toss. In reality you would expect this cube to either already exist or not; there would be no need to load up a data loading application with work like this; work that you would expect to be a one shot deal. That said, it would make sense if this was the sort of application that you needed to deploy across multiple servers and you had to create the same cube and dimensions over and over again. Or alternatively if the cube needed to go through many iterations in development; it's easier to tweak some code than manually tear down and rebuild a cube. (But then, both of those would be just as easy to do in T.I.) If I was building some kind of temporary cube for each run of the application then I'd add cube building code in. For something like this... the only reason it's there is to demonstrate the coding techniques, not because I think it would be useful in production code.

TM1StockWriter? With both of the other classes gone from the package in a real world example, this one would probably have been put into the mainpackage, if indeed I still decided to use a separate class at all.

The MainCode Code
This is the MainCode.java file in its entirety. This is for reference only; we will have to segue off into other classes as we go through it and so I'll be including the relevant extracts from the code below along the way.

Code: Select all

package com.yourcompany.mainpackage;

import java.util.Map;

import com.applix.tm1.TM1Bean;
import com.applix.tm1.TM1Server;
import com.yourcompany.logwriter.LogWriter;
import com.yourcompany.stockreader.StockReader;
import com.yourcompany.tm1writer.TM1Connector;
import com.yourcompany.tm1writer.TM1StockCubeCreator;
import com.yourcompany.tm1writer.TM1StockWriter;

import yahoofinance.Stock;

public class MainCode {

	private static String gsAdminServer = "";
	private static String gsLogPath = ""; 
	private static LogWriter goLogWriter = null;
	
	public static void main(String[] stocksArray) {

		boolean bServerConnected = false;
		boolean bContinue = true;
		TM1Bean oTM1Bean = null;
		TM1Server oTM1Server = null;
		TM1StockWriter oTM1sw = null;
		int iDebug = 1;
		
		final String SC_PROCEDURE_NAME = "main Process";
		
		// I'm working on multiple computers, and I'm sick of having to flip 
		// to different admin servers.
		// NB: This line only works on Windows.
		String sCompName = System.getenv("COMPUTERNAME");
		gsAdminServer = sCompName;
	
		//DO NOT FALL INTO THE TRAP OF DOING A STRAIGHT COMPARISON!
		// Remember that a string is a complex object and you are sometimes testing
		// whether it's the same OBJECT, not the same VALUE.
		if(gsAdminServer.equals("PCD12019999")){
			// Note the reversed slashes.
			gsLogPath = "F:/Users/TM1Administrator/TM1/Servers/RPO/LoggingFiles/"; 
		}else if(gsAdminServer.equals("PCM13019998")){
			gsLogPath = "C:/Users/TM1Administrator/TM1/Servers/RPO/LoggingFiles/";
		}else{
			System.out.println("Please set the log path; unknown computer.");
		}
		
		// ------------------------------------------------------------
		// Create the Log Writer object.
		goLogWriter = new LogWriter("Post5000" ,gsLogPath);
				
		// Create the hashmap that will hold the values returned from the Yahoo API.
		Map<String, Stock> stocks = null;
		
		// Get the information about the current stock prices.
		try {
			stocks = StockReader.getStocksFromArray(stocksArray,goLogWriter,iDebug );
		} catch (Exception e) {
			bContinue = false;
			goLogWriter.outputToLog(SC_PROCEDURE_NAME + ": " + e.getMessage());
			goLogWriter.outputToLog(SC_PROCEDURE_NAME + ": Unable to do anything without stock data; aborting.");
		}

		if (bContinue){
			TM1Connector oTM1Conn = new TM1Connector();
			try {
				oTM1Bean = oTM1Conn.getTM1Bean(gsAdminServer);
				oTM1Server = oTM1Conn.getServerConnection(oTM1Bean, "RPO", "Admin", "apple");
				if(oTM1Server.isError()){
					bContinue = false;
					goLogWriter.outputToLog(SC_PROCEDURE_NAME + ": " + oTM1Server.getErrorMessage());
					goLogWriter.outputToLog(SC_PROCEDURE_NAME + ": Unable to do anything without getting a reference to the server; aborting.");
				}else{
					bServerConnected= true;
				}

			} catch (Exception e) {
				bContinue = false;
				goLogWriter.outputToLog(e.getMessage());
				goLogWriter.outputToLog(SC_PROCEDURE_NAME + ": Failed to connect to server; aborting.");
			}
		}

		if(bContinue){
			try {
				TM1StockCubeCreator.createStockDataCube(oTM1Server, goLogWriter,iDebug);
			} catch (Exception e) {
				bContinue = false;
				goLogWriter.outputToLog(SC_PROCEDURE_NAME + ": " + e.getMessage());
				goLogWriter.outputToLog(SC_PROCEDURE_NAME + ": " + "Error occurred when creating cube; aborting.");
			}			
		}
		
		if(bContinue){
			try {
				oTM1sw = new TM1StockWriter();
				oTM1sw.prepareDimensions(stocks, oTM1Server, goLogWriter);
			} catch (Exception e) {
				bContinue = false;
				goLogWriter.outputToLog(SC_PROCEDURE_NAME + ": " + e.getMessage());
				goLogWriter.outputToLog(SC_PROCEDURE_NAME + ": Error occurred when updating dimensions; aborting.");
			}
			
		}

		if(bContinue){
			try {
				oTM1sw.writeToCube(stocks, oTM1Server, goLogWriter, iDebug);
			} catch (Exception e) {
				bContinue = false;
				goLogWriter.outputToLog(SC_PROCEDURE_NAME + ": " + e.getMessage());
				goLogWriter.outputToLog(SC_PROCEDURE_NAME + ": " + "Error occurred when writing data; aborting.");
			}			
		}

		if (bServerConnected){
			try {
				oTM1Bean.destroySessionAndCloseConnection(oTM1Server);
			} catch (Exception e) {
				e.printStackTrace();
			}
		}

		stocks = null;
	}
}
The Heading Section Of The Class
As mentioned previously, we have to begin with a package declaration stating what the name of the code's package is.

The import statements we'll come back to.

Eventually we get to public class MainCode which of course is the start of the class definition. All of the methods and fields relating to that sit within the braces that are opened at the end of that line.

We begin with three private variables; two of them store String objects, and one an instance of our own LogWriter class. These are all initialised to empty. Because they're declared outside any method, they are available to any method in this class. (Though since we have only one, that's a tad academic. As I said, a lot of this example is about explaining how to do things rather than actually needing to do them.)

The fact that they are private means that only methods in the MainCode class can access them.

The fact that they are static means that there is no need to create an instance of the MainCode class before another class can call them. Hang on, they're private, other classes can't call them anyway.

Correct.

Why are they static then, what's the point?

They have to be static because they're used in the main() procedure. The rule is that if a variable is used in a static procedure (which main() is), that variable must itself be static. The reason isn't as obvious with main(), but it makes sense in other static methods in other classes. Think about it, if a method can be called without an instance being created, then any variables that the method uses must also be available otherwise the method wouldn't work. And the only way that that can happen is if the variables are static, and not dependent on an instance of the class having been created.

I did break my own naming conventions here; normally I use a g prefix for "global" meaning public, which these originally were, but then I decided that I'd get more value out of explaining the static principle and how to pass them between methods. I just didn't bother renaming them. (Once more, "this is not a production application".) The "s" in the second position obviously stands for "String", and the "o" for Object in the case of goLogWriter.

The main() Method
We begin with declaring a few variables. I want them declared here so that they're available throughout the procedure.

Code: Select all

boolean bContinue = true;
boolean bServerConnected = false;
TM1Bean oTM1Bean = null;
TM1Server oTM1Server = null;
TM1StockWriter oTM1sw = null;
int iDebug = 1;
		
final String SC_PROCEDURE_NAME = "main Process";
Remember: If you declare a variable inside a code block, it only exists inside that code block. This can cause scope problems, even in try/catch blocks. Remember that the syntax of declaring a variable in Java is:

Code: Select all

Data_Type Variable_Name = Optional_Initial_Value
You'll see a lot of code which declares an object variable (using a capital initial letter, obviously) and then uses the same name, but with a lower case initial letter for the variable name. I'm not a fan of that and prefer to prefix any such variables with a lower case o for Object. (By convention variables should have a lower case initial, then use Camel Case for the rest of the name.)

Of those variables:
  • boolean (lower case, of course) data type bContinue is initialised with the keyword true. (Not a numeric value, true.) This is used to determine whether it's OK to process the next block of code.
  • boolean bServerConnected is used to determine whether we made a successful connection to the server and, if so, whether to try to close the connection down at the end.
  • TM1Bean oTM1Bean: Here we meet the first of the objects coming out of the TM1 Java API library. A TM1Bean object is roughly the equivalent of a user handle under the classic API. You set the Admin Host, the (typically SSL) port that you'll communicate through, and get a list of all of the servers that are registered with that Admin Server. We shall see how this works shortly. This object is not specific to any particular server connection.
  • TM1Server oTM1Server: This is an object (again from the TM1 Java API library) that holds a reference to the server that we're connected to. Once we're connected this object becomes our gateway to all of the objects on that server.
  • TM1StockWriter oTM1sw: An instance of the TM1StockWriter class that has been written for this project. In real life? There's absolutely no reason why the TM1StockWriter's methods can't be static. However I want to demonstrate both static and instance methods, so here we are.
  • int iDebug is a value that I set to indicate if I want to generate debug messages. In theory it's only for development. In reality I would want to be able to set this outside of coding it. There is a bunch of ways to do that; an .ini file or a Registry entry are two that come to mind. But again this is not a comprehensive course on Java so I've just hard coded it here.
  • final string SC_PROCEDURE_NAME: As I mentioned previously Java does not have constants as VB has. The equivalent is to declare a string variable and mark it as final. De facto constants always bear upper case names.
The following section was written out of convenience for me but also serves to illustrate if blocks and String comparisons:

Code: Select all

		String sCompName = System.getenv("COMPUTERNAME");
		gsAdminServer = sCompName;
	
		//DO NOT FALL INTO THE TRAP OF DOING A STRAIGHT COMPARISON!
		// Remember that a string is a complex object and you are sometimes testing
		// whether it's the same OBJECT, not the same VALUE.
		if(gsAdminServer.equals("PCD12019999")){
			// Note the reversed slashes.
			gsLogPath = "F:/Users/TM1Administrator/TM1/Servers/RPO/LoggingFiles/"; 
		}else if(gsAdminServer.equals("PCM13019998")){
			gsLogPath = "C:/Users/TM1Administrator/TM1/Servers/RPO/LoggingFiles/";
		}else{
			System.out.println("Please set the log path; unknown computer.");
		}
The System.getenv (get environment) line is something of a cheat because as the note shows, it works only on Windows. But then it doesn't need to work anywhere else and to be honest there wouldn't even be a need for it to work anywhere outside of development. I only needed it because I was working on both a 9.5.2 and a 10.2.2 server and needed to keep flipping between the log file paths.

if Blocks And String Comparisons
The if (always lower case) condition is enclosed in round brackets. As previously mentioned you should always use the String class' .equals method to compare two strings. (The reasons were given way back in Part 8.) If the comparison yields true then we execute the code block inside the following set of braces, which simply assigns the path of the server's logging directory to the gsLogPath variable.

If the if() block's conditional test returns false, it jumps down to the test of the else if() statement which checks whether it's the other machine. Again this block's code is between its braces.

If neither of those conditions are met, it jumps down to the else block which has no conditional test; it simply executes. In this case it spits an error message out to the console. It doesn't quit because that's not a critical error; the rest of the code can run without a valid log path though obviously there will be no errors reported to the log folder.

In real life of course you wouldn't hard code the paths like this and you certainly wouldn't hard code the machine names to check against. There would be a number of options such as getting the code to read the log location from an .ini file or the registry, or even passing it into main() as one of the string arguments. I didn't go down that last route because I didn't want to overcomplicate the handling of the String array that main() receives. (As things stand we know that it's stock codes, and nothing but stock codes.)

Creating Our First Object, And Constructor Methods

Code: Select all

// Create the Log Writer object.
goLogWriter = new LogWriter("Post5000" ,gsLogPath);
Now we create a new instance of the LogWriter class and assign it to the object variable goLogWriter (global object, LogWriter). Note the use of the new keyword to do that. You don't always do that; some classes have what are known as "factory methods" which return instances of other classes or possibly their own class. However our classes are simple, straightforward ones and new is the way we go.

Also there is a difference between creating a completely new object as we're doing here, and getting a reference to an object that already exists as we do with most of the TM1 objects.

As previously mentioned all classes have at least one constructor method, even if it's an implicit one which has no arguments. The constructor method always has the same name as the class itself. Ours, however, has two explicitly defined ones and the one that is used depends on the values that we pass, as we shall soon see.

Import Statements
It's time to look at these. You may note that one of the import statements is:

Code: Select all

import com.yourcompany.logwriter.LogWriter;
If you make a call to any object which is not in the same package (as we did in creating a new LogWriter), you have to declare an import statement to get a reference to the code. Obviously a name is not enough since there could be many classes with the same name. As a result the import statement needs to include the whole package name.

If you have added the class' .jar file to the build path (or, obviously, the class is in the current project) you need only press [Ctrl]+[Space] in Eclipse to generate the import statement.
User avatar
Alan Kirk
Site Admin
Posts: 6606
Joined: Sun May 11, 2008 2:30 am
OLAP Product: TM1
Version: PA2.0.9.18 Classic NO PAW!
Excel Version: 2013 and Office 365
Location: Sydney, Australia
Contact:

Introduction To The Client Side Java API

Post by Alan Kirk »

Part 20 - Looking At TheLogWriter Class
Once again the full code of the class is shown for reference:

Code: Select all

package com.yourcompany.logwriter;

import java.io.BufferedWriter;
import java.io.IOException;
import java.nio.charset.Charset;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.nio.file.StandardOpenOption;
import java.util.Calendar;

public class LogWriter {

	private String gsOutputPathUsed = "";
	private String gsOutputFileName ="";
	private boolean bPathOK = false;
	private BufferedWriter bwOfLog = null;
	
	public LogWriter(String projectName, 
			String pathOfOutput){
		initialiseLogWriter(projectName, pathOfOutput, 0);
	}
	
	public LogWriter(String projectName, 
			String pathOfOutput, int debugMode){
		initialiseLogWriter(projectName, pathOfOutput, debugMode);
	}	

	public int outputToLog(String stringToWrite){	
		return (outputToLog(stringToWrite,0));		
	}
	public int outputToLog(String stringToWrite, 
			int debugMode ){
		
		int iReturn = 0;
		
		try {
			if (!bPathOK) {
				System.out.println(stringToWrite);
			} else {
				Path pathOfLog = Paths.get(gsOutputPathUsed + gsOutputFileName);
				Charset charSetOfLog = Charset.forName("US-ASCII");
				bwOfLog = Files.newBufferedWriter(pathOfLog, charSetOfLog,StandardOpenOption.CREATE, StandardOpenOption.APPEND);
				bwOfLog.append(stringToWrite, 0, stringToWrite.length());
				iReturn = stringToWrite.length();
				if(iReturn==0){
					System.out.println("You realise that the message was empty?");
				}
				bwOfLog.newLine();
				bwOfLog.close();
				if(debugMode!=0){System.out.println("I have just closed the file after writing " + stringToWrite);}
				
			}
		} catch (Exception e) {
			// Not much else we can do.
			e.printStackTrace();
			System.out.println("Original message: " + stringToWrite);
		} finally {
			try {
				bwOfLog.close();
			} catch (IOException e) {
				// Like we can do anything about this...
				e.printStackTrace();
			}
		}
		return iReturn;		
	}

	private void initialiseLogWriter(String projectName, 
			String pathOfOutput, int debugMode){
		Calendar cal = Calendar.getInstance();
        String sDateTime = "";
        sDateTime = cal.get(Calendar.YEAR)
        	+ "_" + String.format("%02d", (cal.get(Calendar.MONTH) + 1)) 
        	+ "_" + String.format("%02d", (cal.get(Calendar.DAY_OF_MONTH)))
        	+ "_" + String.format("%02d", (cal.get(Calendar.HOUR_OF_DAY)))
        	+ "_" + String.format("%02d", (cal.get(Calendar.MINUTE)))
        	+ "_" + String.format("%02d", (cal.get(Calendar.SECOND)));
        		
		gsOutputFileName= projectName +  "_" + sDateTime + ".txt"; 
		
		Path path = Paths.get(pathOfOutput);
		Boolean bPathExists = Files.isDirectory(path);
		if(bPathExists){
			bPathOK = true;
			gsOutputPathUsed = pathOfOutput;
			if(debugMode!=0){System.out.println("Output path OK.");}
		}else{
			//TODO:Get a temp file for output.
			// In the meantime it'll just go to the console.
			// I haven't written this code to avoid overcomplicating
			// the example.
			if(debugMode!=0){System.out.println("Output path invalid.");}
		}
		
	}
	
}
As always the class begins with its package declaration, followed by the import statements for all of the classes that it may refer to. Note that this time it includes a specific subclass of the Exception class; java.io.IOException.

You may correctly surmise from this that not all packages use reverse domain name notation. Some of the core language packages are under java.lang, java.util and java.io. java.nio refers to the "new input output" library that was introduced with version 7; it made dealing with files much easier than in previous versions.

After the class definition we begin with some more private String variables. Note that these are not static unlike the ones that we saw in the MainCode class. That's because none of this class' methods are static; they can only be called when the class has been instantiated and, in doing so, received information about the output path and the project name so that it can determine the filename and path of the log file. This time there is a reason for making these class level, and that's because these ones really are shared between the various methods and preserved within the object instance in between method calls.

After that we have two methods which have the same name as the class, and these of course are our constructor methods. The first takes two arguments:

Code: Select all

public LogWriter(String projectName, String pathOfOutput){
The second takes three:

Code: Select all

public LogWriter(String projectName, String pathOfOutput, int debugMode){
Both of them do the same thing, which is to call a private method called initialiseLogWriter. That method has the same arguments as the second of the constructor methods.

The first constructor method passes a zero value to the int debugMode argument of the private method. In this way you can create what are effectively optional arguments with a default value.

If we look now at the

Code: Select all

private void initialiseLogWriter(String projectName, String pathOfOutput, int debugMode){
method, it begins by getting a calendar object. I mentioned previously that not all new objects are created with the new keyword but rather are constructed with factory methods, and this is an example of that. As you can guess, the .getInstance method is a static member of the class, and it returns a new class itself. By default the Calendar object that I receive is populated with the current date and time.

The Calendar object that I'm using is one of the older classes and not part of the new time methodology in Java version 8, which you should definitely look into for your own use. I used it here because it's used by the Yahoo library and I wanted to stay consistent.

The Calendar class has a method called .get which allows you to retrieve particular segments of the date, which is what I'm doing here.

Code: Select all

cal.get(Calendar.YEAR)
for example is returning, as you would expect, the year component of the date. Calendar.YEAR is an enumerated constant defined in the Calendar class. (The fact that it's a constant is reflected in the use of all capital letters.) Just as with enumerated constants in VBA this means that you don't have to remember what the actual code is for that date component, you simply pass in the constant with the appropriate name. When you press the period key after Calendar (assuming that you told Eclipse to import the class first by pressing [Ctrl]+[Space] the first time you used it) you'll get a list of all of the methods and properties, including the enumerated values such as the ones shown above. These are of course coming directly from the Calendar class rather than the cal instance that was created from it.

The YEAR value returned is a numeric but is implicitly converted to a String because of the + operator adding it to the other strings.

Note the fact that I need to add 1 to the month component. As I mentioned the older date objects used base zero for the month, so that's one to watch out for.

The String class has many formatting options. In this case I'm calling the .format method to format the date components and passing in the string "%02d" which essentially means "give me two characters for this number, with a leading zero if necessary". Check the documentation for the .format method for other options.

The end result is that I get a timestamp string in the sDateTime variable. We append that to the project name that was passed in and whack a .txt extension on the end, which gives us the name of the text file that we're writing to. That's stored in gsOutputFileName for future use.

We then use the static .get method of an object named Paths to try to get a Path object for the directory name that has been passed into the constructor. Both Paths and Path are part of the new java.nio library introduced in Java 7 to improve file handling operations, and you should look up the documentation on them if you need to do anything substantial with file operations. Note that in this case I've violated that naming principle that I said I didn't like (by creating an object of type Path (capital initial) named path (lower case initial)) but that's only because the variable won't be around for very long so it really doesn't matter.

We pass that path object to the static .isDirectory method of the Files class to see whether the path that was passed in to the constructor method not only exists, but is in fact a directory rather than a file.

If it does we set the bPathOK variable to true and assign the path to the gsOutputPathUsed variable. It will then be used when we come to write the output.

Following that I have a single line if() block:

Code: Select all

if(debugMode!=0){System.out.println("Output path OK.");}
In this we see the != (not equal to) operator, so we only do the statement in braces if debugMode is not zero. If that's the case the code to be done (in this case outputting a line to the console) is contained within the ubiquitous braces.

If the path is not valid, the else block is executed. I had originally planned to put some code in there to get a temporary file but realised that it would be drifting too far off our topic. What will therefore happen is that the class level bPathOK value will remain false, so any attempt to call one of the writer methods will result in the output going to the console. That obviously isn't something that you would do in a production grade system.

The outputToLog Method
There is really only one method to this class, which is the

Code: Select all

public int outputToLog(String stringToWrite, int debugMode){
one. Note the lower case first initial indicating that it's a method rather than an object.

This method is overloaded by including a second syntax which omits the debugMode argument. If that one is called then it simply calls the two argument version of the method (and returns the value that it gets back from that call), passing 0 as the debugMode argument.

The primary argument that is passed is a String object named stringToWrite.

I would hope that a lot of the syntax would be familiar by now so I'll just cover the highlights:
  • The Path object returned from the Paths class' .get method doesn't just refer to folders, it can refer to files as well which is why I'm passing it the complete path and filename.
  • Charset is an object that allows internationalisation of the output. In our case all we want to use is the US ASCII character set. We'll pass the resulting object into the file writer object when we create it.
  • The file creation object is an object named BufferedWriter. A buffered writer as the name suggests is actually intended for heavy duty writing of significant volumes of content repeatedly. Since in this method we're just opening the file, writing one line of text then closing it again, it's a bit like taxiing a Learjet down to the corner store to get a litre of milk; there are other ways to get it done. However my main intent is to make you aware of its existence.
  • In creating the BufferedWriter object I pass the Path object representing the log file (which obviously won't exist yet on the first call, but that's OK), the Charset object that specifies what characters the content will be written in, and two constant arguments from a class called StandardOpenOption; the .CREATE constant and the .APPEND constant. These essentially specify (as you would expect) that if the file doesn't exist the BufferedWriter should create it, and if it does it should append to it.
  • I recall from my old C days that you had to flush some buffers after you were done with them, but I don't think that it's necessary with this object. I could be wrong, but it's never given me any problems.
  • The BufferedWriter is embedded within a try/catch block. If anything goes wrong it will jump to the catch block, where an Exception object named e will be waiting. I call the e.printStackTrace method which essentially dumps all of the content out to the console window, though again this is mainly to show you that such a method exists.
  • In the finally block I try to close the BufferedWriter, but if anything goes wrong I can't really do anything about it.
  • The method is returning an int, and until I put a return statement in returning such a value I'll have a compile error. In this case I'm returning the length of the string that was written, not because it was useful, more because it's a demonstration of a return statement. Remember that if you use the first syntax of the method (the one without the debugMode argument) that value will be returned to that method, which will then pass it back to the original caller. That's because in the first syntax I'm simply defining the code as "return(whatever I get back from the call to the second syntax)".
Locked