text:
WebObjects/Web Applications/Development/WO Component/Code Template and WODs

Overview
WebObjects was designed with a Model-View-Controller architecture. This means that your application is separated into several distinct roles. Your EOModels and their EOEntities constitute the model layer. The Views and Controllers are implemented with WOElements and WOComponents, which area spread across three different pieces: Code, Templates, and WODs. One of the things interesting aspects of WebObjects is that the component architecture does not presume to produce HTML. You can use the same component system to create HTML, XML, CSS, XSLT, emails, or any other type of dynamic data.

Code
Whether you extend WODynamicElement or WOComponent, you always have a Java class that implements your component's logic, and optionally stores its state. By default, the name of your Java class determines how you will refer to your Component throughout your application, which you will see several examples of later. There are a few key differences between implementing WODynamicElements and WOComponents.

WODynamicElements
If you are building a WODynamicElement, your class will not have a Template or WOD file. Any output is generated in code directly. WODynamicElements also can not assume that there will be an instance per usage, rather a single instance of your class may be concurrently serving multiple Components. As a result, your WODynamicElement must be completely thread-safe. These attributes make WODynamicElements ideal for producing relatively small output or output that is "computed." If you find yourself writing large amounts of code to handle outputting data, you may want to consider the benefits of templates that come from building WOComponents.

There are three primary methods that you may implement in your WODynamicElement depending on the type of interaction your element provides or requires:

 * takeValuesFromRequest
 * invokeAction
 * appendToResponse

For more information on the execution order of these methods at runtime, read the Request-Response Loop section.

To receive values from the request (for instance, form data), implement the takeValuesFromRequest method. If your WODynamicElement responds to user actions, you must implement the invokeAction method. And if your WODynamicElement generates output, you must implement the appendToResponse method. At runtime, your WODynamicElement will be wired up to a WOComponent that you can access via the WOContext that is passed into each of the three methods above, which can be used to resolve bindings in a particular appearance on a page. For example, to retrieve the value of a binding, it might look like:

private WOAssociation myNameAssociation; ... public void appendToResponse(WOResponse _response, WOContext _context) { WOComponent component = _context.component; String name = (String) myNameAssociation.valueInComponent(component); super.appendToResponse(_response, _context); }

This same pattern is used for Stateless WOComponents. WODynamicElement can provide a .api file to describe its bindings. For more information on .api files, see the .API File section below.

WOComponents
WOComponents add several major capabilities above and beyond WODynamicElements. The primary additional capability is support for templating. When you create WOComponent, you can also create a ".wo" folder, which contains three files: a .html or .xml template, a .wod binding declaration, and a .woo file. Like WODynamicElements, WOComponents can also provide an optional .api file (as described below). The code for a WOComponent can be a simple as simply declaring that your class extends WOComponent along with its constructor. For example:

public class MyComponent extends WOComponent { public MyComponent(WOContext _context) { super(_context); } }

The same three core methods described above for WODynamicElements also exist for a WOComponent, however because WOComponents can be stateful, you can also declare instance variables (ivars) in your component that you can bind to. Each time your WOComponent is used on a page, a new instance is created, which is another large distinction as compared to a WODynamicElement. It is not necessary for your WOComponents to be thread-safe.

Templates
By default, WOComponent templates can be either .html or .xml files. WebObjects templates separate all binding declarations into a separate file called a .WOD file, which is different than many other web frameworks. As a result, the templates are generally much easier to read and can often be more easily given to a designer to work on without the risk of code-related bindings being modified by accident. WOComponents are declared in the template using a "webobject tag", which looks like an HTML tag:

The name declared in the WebObject tag will be used as a key to lookup the corresponding binding definition in your WOD file.

WOD Files
WOD files (Web Object Declaration?) define the bindings for each of the component references in the template. For instance, if the reference above was connected to a "public String personName" method on your component, the WOD declaration might look like:

PersonName : WOString { value = personName; }

There are several pieces to this declaration. The first token is the WebObject tag name which appears in the template. These values much match exactly for WebObjects to resolve the binding information. If you reference a name in your template and do not declare a corresponding WOD entry, WebObjects will throw an exception at runtime.

The second token, after the colon, is the name of the component or element to instantiate in your template. This name is resolved using NSBundle lookup rules, which by default will find any class that has the same name as the token, excluding package name. For instance, WOString might actually be "com.webobjects.appserver._private.WOString", but the name of the class without its package name is "WOString". This is important to note, because it means that if you are using this syntax, you must uniquely name your classes across all packages (an Objective-C heritage, where the concept of a package did not exist). You can, however, choose to fully qualify your class names, in which case the "WOString" in the example above would become "com.webobjects.appserver._private.WOString" and there would be no ambiguity in class resolution. It is possible to override the normal NSBundle lookup process by using a method on NSUtilities:

NSUtilities._setClassForName(com.bla.TestComponent.class, "TestComponent");

This call would bind the name "TestComponent" appearing in a WOD reference to the class com.bla.TestComponent, avoiding any further class "hunt". This is also useful to replace internal component implements with your own. For instance, you could replace the implementation of WOString with your own class -- a capability that Project WOnder makes extensive use of to provide bug fixes and enhancements to the core components.

Moving along in the WOD file, inside of curly braces, you can provide a series of key/value pairs, each ending with a semicolon as a separator. The left hand side of the equals is the binding name. The binding name is resolved on a WOComponent by attempting to find a mutator method or field on your WOComponent class that matches one of the naming conventions: public void setValue(Xxx param), public void _setValue(Xxx param), public Xxx value, public Xxx _value (assuming the example above of a binding named "value"). So if your WOComponent had a setValue method, and automatic binding synchronization was enabled, the setValue method would called passing in the evaluated value of the right hand side of the binding. In the example above, the right hand side is "personName". If personName was actually in quotes in example above, it would be considered a string literal and would be equivalent to calling setValue("personName").

However, personName has no quotes, and is thus interpreted with Key-Value-Coding (KVC). Key-Value-Coding allows you to string together a series of accessor method calls or field references as a string that WebObjects will dynamically resolve using Java reflection. For instance, in the example above, personName would attempt to find any accessor or field named: public String getPersonName, public String _getPersonName, public String personName, public String _personName, public String personName, public String _personName. So if your Java class had a public String getPersonName method, the return value of that method would be passed into the setValue(..) method of your WOComponent. Where KVC gets more interesting is that you can construct a series of method calls. For instance, instead of a person, imagine that your WOSession had a "public Person person" method, which had a "public Address address" method, which had a "public String zipCode" method. Your WOD file binding could look like "value = session.person.address.zipCode;", which would bind the zipcode of the address of the person in your session to value. Even more interesting are the NSArray operations that Foundation provides. For instance, if your session had a "public NSArray purchaseAmounts" that returned an array of BigDecimals, you could refer to the binding "value = session.purchaseAmounts.@sum", which would return the sum of the values of the array. There are several other Array operations available in the Foundation classes, and Project Wonder provides even more in its ERXArrayUtilities class. For even more advanced KVC capabilities, read the WOOgnl section of Project Wonder.

A single WOD entry can contain several binding declarations, and a WOD file can contain many entries. For example, here is an excerpt from a real WOD file:

FilterAction : WOSubmitButton { action = filter; value = "filter"; } EditAction : WOHyperlink { action = editRequest; } NoRequestsConditional : WOConditional { condition = requestsDisplayGroup.allObjects.count; negate = true; }

WOO Files
At a minimum, a WOO file declares the WO version and the Template's character encoding. As an example, a bare bones WOO file might like look:

{   "WebObjects Release" = "WebObjects 5.0"; encoding = NSMacOSRomanStringEncoding; }

Additionally, a WOO file can contain definitions of instantiated objects that your WO component can refer to. If you build your components with WOBuilder, you will find the definition of the attributes of your WODisplayGroups in your component's WOO file.

API Files
API files are optional files that appear in the same folder as your .WO, and provides metadata about your component that can be used by an IDE to provide a better experience for your components' users. For instance, API files declare all of the bindings for your component, the binding types (boolean, date, etc), along with a fairly extensive XML language that is used to define binding validation.

As an example:

In this example, the API defines the bindings at the top, along with a series of validations. The validation message is displayed in the IDE when the declarations inside the validation evaluate to "true". For instance, for the first validation, the validation message appears if the "id" value is not bound. In the second case, the message appears if the "list" value is bound but "listItemIDKeyPath" is NOT bound.


text:
User:Rama's Arrow

Hi, I'm Nirav Maurya, Rama's Arrow. I'm primarily active on Wikipedia and I actively start contributions to Wikibooks after a brief interval. Rama's Arrow 03:20, 21 May 2006 (UTC)

license: Creative Commons - Attribution Share-Alike - https://creativecommons.org/licenses/by-sa/4.0/

title: User:Rama's Arrow


text:
User talk:Rama's Arrow

Hi, I'm Nirav Maurya, Rama's Arrow. I'm primarily active on Wikipedia and I actively start contributions to Wikibooks after a brief interval. Rama's Arrow 03:20, 21 May 2006 (UTC)

title: User talk:Rama's Arrow


text:
Talk:Artificial Intelligence/Logic

Stub
I see no information here on this subject except a link to materials on formal logic. Which is great; but, if there is no additional info, please assert this so that users doesn't spend uncessary time trying to figure out how to display it. - thanks!!!!

something like:

"This document is a stub, no info has been included at this time." —The preceding unsigned comment was added by 216.192.181.4 (talk • contribs).

 * I've added the standard stub notice. --Mrwojo (talk) 22:20, 31 December 2008 (UTC)

title: Talk:Artificial Intelligence/Logic


text:
User:Whopper~enwikibooks

Whopper - this is my main page.

title: User:Whopper~enwikibooks


text:
User talk:Whopper~enwikibooks

Whopper - this is my main talk page, go here for a faster response from me.

Your account will be renamed
Hello,

The developer team at Wikimedia is making some changes to how accounts work, as part of our on-going efforts to provide new and better tools for our users like cross-wiki notifications. These changes will mean you have the same account name everywhere. This will let us give you new features that will help you edit and discuss better, and allow more flexible user permissions for tools. One of the side-effects of this is that user accounts will now have to be unique across all 900 Wikimedia wikis. See the announcement for more information.

Unfortunately, your account clashes with another account also called Whopper. To make sure that both of you can use all Wikimedia projects in future, we have reserved the name Whopper~enwikibooks that only you will have. If you like it, you don't have to do anything. If you do not like it, you can pick out a different name.

Your account will still work as before, and you will be credited for all your edits made so far, but you will have to use the new account name when you log in.

Sorry for the inconvenience.

Yours, Keegan Peterzell Community Liaison, Wikimedia Foundation 23:51, 17 March 2015 (UTC)

Renamed
 This account has been renamed as part of single-user login finalisation. If you own this account you can |log in using your previous username and password for more information. If you do not like this account's new name, you can choose your own using this form after logging in: . -- Keegan (WMF) (talk) 05:38, 19 April 2015 (UTC)

title: User talk:Whopper~enwikibooks


text:
User talk:70.118.110.109

Do not insert nonsense into Wikibooks. Thanks. -- LV (Dark Mark) 06:20, 21 May 2006 (UTC)

title: User talk:70.118.110.109


text:
Wikijunior:Kings and Queens of England/Introduction

Welcome to the Wikijunior book on Kings and Queens of England.

In this book, we will start by looking at the very first Anglo-Saxon Kings of England. We will then move on to show how the Crown changed hands many times as a result of conquest. We see some powerful kings and some weak ones. We see how the Crown has battled Parliament. We look at the period where power finally did transfer to Parliament through to the times of our current queen, Elizabeth II. At the end we will also look at who the next kings of England may be.

We will find out about eleven Kings called Edward and nine called Henry and a nine-day queen and King Philip, who most people have now forgotten about. But first let's start way back in 871 with the Anglo-Saxons and the only king of England to be called "Great," Alfred.


text:
Talk:Calculus/Parametric Differentiation

=Slope of parametric Equations= I reordered the form of the equation for solving for $${dy\over dx}$$, in order to make it seem more as a proof and follow logical progression of thought. Aznph8playa 11:03, 21 May 2006 (UTC)

=Concavity of Parametric Equations= This section is confusing and could be expanded upon / reworded. 147.9.166.193 (talk) 15:45, 24 March 2010 (UTC)


text:
User:Orbisonitrum

Just recently switched to blender from povray. Currently working as a software developer for a small governmental agency in a slightly larger country.

title: User:Orbisonitrum


text:
WebObjects/EOF/Using EOF/Primary Keys

Accessing Primary Keys
When your primary key is not visible as a class property, the documented way to obtain the primary key values is with EOUtilities.primaryKeyForObject. This works, but in the case where you have a single non-compound primary key, you need a few more lines of code to pull out this pk value. More troubling, the EOUtilities method, when called with a fault, will cause the fault to be resolved. The fault does not really need to be resolved to get the primary key, as the pk value is present even in the fault.

Here's a method that works for EOs which have a single Integer primary key, to pull out that pk value, and without resolving a fault (possibly causing a trip to the db) when called on a fault value:

public static Integer singleIntegerPrimaryKeyForObject(EOEnterpriseObject eo) { EOEditingContext ec = eo.editingContext; if (ec == null) { //you don't have an EC! Bad EO. We can do nothing. return null; }       EOGlobalID gid = ec.globalIDForObject(eo); if (gid.isTemporary) { //no pk yet assigned return null; }       EOKeyGlobalID kGid = (EOKeyGlobalID) gid; NSArray keyValues = kGid.keyValuesArray; if (keyValues.count != 1 ||           ! (keyValues.objectAtIndex(0) instanceof Integer)) return null; return (Integer) keyValues.objectAtIndex(0); }

You can also use the EOUtilities method but please note that, at least as of WO5, there are two somewhat undesirable aspects of this EOUtilities code:
 * If the EO is a fault, calling the EOUtilities method will cause the fault to be resolved, possibly requiring a trip to the db, even though pk info is already present in the fault and the trip to the db is really unnecessary at this point.
 * If the EO has just been inserted and not yet committed, it does not have a pk yet, and the EOUtilities code will throw an exception. And it's not a logical exception corresponding to "no pk yet", but rather some uncaught exception thrown by some surprised Apple code.

Assigning Primary Keys
Usually EOF doesn't get around to generating/inserting primary keys until you actually saveChanges on an EOEditingContext containing newly created about-to-be saved EOEnterpriseObjects.

But sometimes you want to assign a permanent pk as soon as you create the EOEnterpriseObject, not wait until it's actually committed to the db. Here's some code to do that, which, in a given situation, will generate the pk in the same way that EOF would have generated it later, but do it right away. [Thanks to Pierre Barnard]

public void _insertObjectWithGlobalID(EOEnterpriseObject eo, EOGlobalID globalID) {   EOEntity entity = EOUtilities.entityNamed(this, eo.entityName); EODatabaseContext dbContext = EOUtilities.databaseContextForModelNamed(this, entity.model.name); NSDictionary pkDict = primaryKeyDictionaryForDatabaseContextAndEntity(dbContext, entity); if (pkDict == null || pkDict.count != 1) {     NSLog.err.appendln("Failed to generate primary key for entity " + entity.name + ", or pk is compound."); } else {     Object pk = pkDict.allValues.lastObject; globalID = EOKeyGlobalID.globalIDWithEntityName(entity.name, new Object[] { pk }); }   super._insertObjectWithGlobalID(eo, globalID); } public static NSDictionary primaryKeyDictionaryForDatabaseContextAndEntity(EODatabaseContext dbContext, EOEntity entity) { NSDictionary pk = null; try { dbContext.lock; EOAdaptorChannel adaptorChannel = dbContext.availableChannel.adaptorChannel; if (!adaptorChannel.isOpen) { adaptorChannel.openChannel; }     pk = (NSDictionary) adaptorChannel.primaryKeysForNewRowsWithEntity(1, entity).lastObject; }   catch (Exception e) { NSLog.err.appendln("Can't get primary keys for entity " + entity.name + " " + e); }   finally { dbContext.unlock; return pk; } }

There are 2 insertObjectWithGlobalID methods in EOEditingContext. One has a name prefixed by an underscore. The regular one seems to call that one. For most uses overriding the regular one should be sufficient. With JavaClient however, the regular one is not called. On the server side only the one with the underscore is called. You'll have to override the method with the underscore in the server side context as the above will work only on the server.

If you use nested ECs in a standard WO application you might have to override the same semi-private method should you want the parent EC to handle PK creation.

BTW, I believe EOF does some optimization by retrieving PKs in batches rather than one by one. You lose that when using the above code.

title: WebObjects/EOF/Using EOF/Primary Keys


text:
WebObjects/Web Applications/Development/Development on Windows

Here is a tutorial on installing WebObjects on Windows:

http://www.tetlabors.de/wo/setup_webobjects_on_windows.html

Fixing Auto Launch
''Q. Hi everyone. I've seen several people on the list with the same problem as this, but haven't seen it resolved yet. When launching an app on WO5.2.2 developer on Windows XP, this error message is displayed:''

Your application is not running on a supported development platform. AutoLaunch will not work.

I can cut and paste the URL into a browser manually, but I'm too lazy to do all that 'work' all day long :) I've tried the WOAutoOpenInBrowser setting with no luck. I've also tried setting W2K compatibility mode for project builder and the WOOpenUrl application with no luck.

A. use the following methods in your Application class:

/**  * Calls _isAdditionalForeignSupportedDevelopmentPlatform *   * @see com.webobjects.appserver.WOApplication#_isForeignSupportedDevelopmentPlatform */ public boolean _isForeignSupportedDevelopmentPlatform {   return (super._isForeignSupportedDevelopmentPlatform || _isAdditionalForeignSupportedDevelopmentPlatform); } /**   * Check for Windows XP   * @return true if runs on XP   */ public boolean _isAdditionalForeignSupportedDevelopmentPlatform {   String s = System.getProperty("os.name"); return ( s != null && s.equals("Windows XP") ); }

-- StefanKlein

B. Above method didn't work for me. Adding the following to the Application Class worked.

public boolean _isSupportedDevelopmentPlatform { boolean result = super._isSupportedDevelopmentPlatform; if(!result){ result = System.getProperty("os.name").equals("Windows XP"); }          return result; }

-- Guillaume Luszack


text:
WebObjects/EOF/Modeling/Booleans

Overview
There are many way of modeling Boolean in WO.

I like this one as it does not require any specific code and is very readable in the database:

Caution this only works in WO 5.2 and is broken in WO 5.0 to WO 5.1.4

Here is how I do it. I define the column as a Character Varying of 5 so that I can store "true" or "false". In EOModeler I define the Internal Data Type as custom the class as java.lang.Boolean, the factory method as valueOf and the Conversion method as toString and the init argument as NSString. This works well even if it wastes a little space in the database.

To make it easier to use you should define it as a prototype.

Note - this is also discussed in the book "Practical WebObjects"; on page 42.

Jesse Barnum
Another way to accomplish this is to define your attribute as a Number with 'c' as the value type in EOModeler. This causes the JDBC adaptor to use the getBoolean / setBoolean methods on the ResultSet, so you can use whatever data type you want on the database as long as the JDBC driver supports it.

If you want to write custom accessor methods, you can use Boolean types and EO will automatically do the casting - so this will work:

public Boolean getAttribute { return storedValueForKey("attribute"); } public void setAttribute(Boolean input) { takeStoredValueForKey(input, "attribute"); }

Jonathan Rochkind
Interestingly, in WO 5.2, _regardless_ of how you have set the 'value type' in EOModeler, EOF will sometimes automatically convert any numeric db column to a Boolean value. If you have an attribute called 'someAttribute', and the class for the entity defines a method like "public Boolean someAttribute", then storedValueForKey("someAttribute") will automatically return a Boolean (whether you like it or not!). If the signature is small-b boolean, this is true too. There may be other cases where it is true---it's entirely undocumented and I just figured it out through experiment.

The algorithm used to convert the numeric data to a Boolean seems to be---if the number is 1, return Boolean(true), otherwise (including null) return Boolean(false). null is never returned by storedValueForKey ("someAttribute") in this situation, even if there was a null in the db. Again, it doesn't seem to matter what the 'value type' in the EOModel is.

Jesse Barnum
I'm noticing the exact same thing as Jonathan. However, this 'feature' introduces a problem - the stored snapshot from the database contains the value '1' (I guess the conversion happens after that point), and whenever I save changes to the database, EOF thinks that that attribute has changed from '1' to 'true', and it writes this to the database, even when this is not necessarily appropriate.

Jonathan Rochkind
The problem is worse than that. In addition to what Jesse notes, this 'feature' causes some serious problems with EOF, probably related to the stuff Jesse is talking about. Particularly, in some cases EOF will think an attribute _hasn't_ changed even when it has, and not write anything to the db even when it should. The presence of an 'automatically converted attribute' seems to trigger this problem, but the changed properties that EOF will get confused about may occur in OTHER attributes or even relationships! I can not really explain this; I can not even explain under what circumstances it occurs. But I had a situation where a to-one relationship destination was changed, but EOF would not write the changes on saveChanges. However, removing the 'automatic boolean converted' attributes, making no other changes to the program, made the problem go away. [WO 5.2]

I recommend _avoiding_ the automatic boolean conversion. It causes all sorts of problems. [I filed a request with Apple asking for a way to turn off this automatic boolean conversion, but the engineers said no. I filed my own bug report. Jesse, you should file one about what you note specifically.]

For more description of this bug, see: Weird52EOFBug I know this bug effects 5.2.1; I don't believe it's been fixed in 5.2.2. (written 13 March 2004)

5.2.3 Release Notes
Just noticed this in the Release Notes of 5.2.3 http://docs.info.apple.com/article.html?artnum=107873 --- To-one relationship does not update correctly when destination entity has boolean attribute

Apple Reference 3108071

ISSUE: Upon saving changes, the destination entity with boolean attributes does not commit correctly to the database because the snapshot is in an invalid state.

RESOLUTION: In order to resolve this problem, the behavior of a boolean attribute that is defined with a Number value class and value type of 'c' will now return a Boolean value instead of an Integer value.

Mark Gowdy
My 'zero effort' approach is as follows: In EOModeler, I have an attribute called 'enabled':

Name = 'enabled' Value Class (Java) = 'Boolean' External Type = 'BOOL' (in MySQL it maps to tinyint(1)) Value Type 'c'

EOModeler complains that it needs 'valueFactoryMethodName' and 'adaptorValueConversionMethodName' for this attribute, but it seems that you can just ignore it.

When I generate my java, I get:

public Boolean enabled{ return (Boolean)storedValueForKey("enabled"); } public void setEnabled(Boolean v){ takeStoredValueForKey(v, "enabled"); }

My initial tests look promising, but can you guys try it with 5.2.3+ and your reproduceable case to make sure?

title: WebObjects/EOF/Modeling/Booleans


text:
WebObjects/Web Applications/Development/Calling Commandline Applications

Here, the "cat" and "outputCat" are debugging output categories:

Process process=null; try { process = Runtime.getRuntime.exec(commandLine); OutputStream output = process.getOutputStream; output.write(inputString.getBytes); output.close; process.waitFor; DataInputStream dis=new DataInputStream(process.getInputStream); String output; do { output = dis.readLine; if (output != null) outputCat.debug(output); } while (output != null); dis.close; } catch (IOException e) { cat.error("IOException: " + e.toString); } catch (InterruptedException e) { cat.error("Interrupted process: " + e.toString); } finally { if (process != null) process.destroy; outputCat.debug("Process finished."); }

Note: A number of people have reported problems with process.waitFor on Windows. The WebObjects Development List at Omnigroup has a number of people's workaround code for this problem.

Note 2: The procedure given here, of course, is to call anything executable from the command line from your Java program, not just Perl scripts.

Mike Schrag
process.waitFor is not just a problem on Windows. This code will cause problems. Process maintains buffers for stdout and stderr. If you do not consume those streams, you will run into deadlocks. The proper way to use Runtime.exec is to setup a thread for stderr and a thread for stdout and consume them yourself into your own buffer that does not have the same restrictions that the stock VM has.

There's a good example of this technique on JavaWorld.


text:
WebObjects/Web Applications/Development/Cocoa EO Applications

Overview
''Note: these techniques depend on deprecated technologies. Use at your own risk. -- Mike Schrag''

A CocoaEOApplication (formally, in WO 5.2, Cocoa Enterprise Objects Application, but here simply CEO) is a set of objects which allow you to use the Enterprise Objects Framework (EOF) inside Cocoa. Even though the technology is not yet supported by Apple, it had been around since NeXT was independent.

In its previous versions, CEO uses Objectve-C as its primary language (which is now the primary language of Cocoa), but since WO 5.0 the main language to use EOF is Java. In order to use EOF within Cocoa (in particular, in order to use EOModeler in OSX), Apple developed the so-called Java-Bridge (see e.g., http://cocoadevcentral.com/articles/000024.php ---there was a document entitled "Using the Java Bridge", but at the time I am writing this (Jul 24 2003), it was not available any more in developer.apple.com; however you may find a copy in your hard disk under /Developer/Documentation/JavaBridge/JavaBridge.pdf). In order to develope a CEO, you will have to get used to its mechanisms.

Update (Jun 3, 2005): the JavaBridge.pdf is now in http://developer.apple.com/documentation/Cocoa/Conceptual/Legacy/JavaBridge/JavaBridge.pdf

Getting Started
There are some things you have to be aware when creating a CEO project (this note is for WO 5.2 and has been revisted for 5.2.2 ---and now for WO 5.2.4):

When you launch the Project Builder's wizard and ask for a Cocoa/EO application, it will create an unusable MainMenu.nib file. To solve the problem: close your project; delete the "corrupted" file MainMenu.nib (it is inside the English.lproj folder of your project); launch Interface Builder and create an Empty interface; save it as MainMenu.nib (better with the extension) in the same place where the "corrupted" file was; re-launch Project Builder; Done.

 * .... in WO 5.2.2 for OSX 10.3 (Panther+Xcode), the previous bug was fixed.
 * .... in WO 5.2.4 for OSX 10.4 (Tiger+Xcode), it still works.

The first time you drag an entity from EOModeler into Interface Builder, an EOEditingContext will be created for you (if you drag the entity onto a window, an NSTableView and an EODisplayGroup will also be created; if you do onto the class-viewer, only the EODisplayGroup will be created). The default (not always desirable) is to "fetch on load" all the objects in the entity; if you do not want this, go to the inspector (with the EODisplayGroup selected) and uncheck the corresponding button.

Basically, you are there. You can use all actions of your display groups and your editing context to see and manipulate your database, without writing a single line of code ---in fact, you do not have neither to compile your project; simply connect your buttons and run the "Test Interface" command from the file menu (or cmd+r from the keyboard) and you will see your database showing up.

Of course, if you are an experienced programmer and want to add some "logic" to your app, you can do it. The first thing you have to do, is to choose a language; I prefer Obj-C, but also Java can be used... in fact it will be used even if you decide not to. The EOF had been written in pure Java so, even if you do not write a single line of code in Java (which some times seems unavoidable), you will be using it via the Java Bridge. I'll be back on this later...

A "tutorial"
Following the recomendation of a virtual-friend (Arturo Perez), I decided to add a simple guide to build up a CEO.

It is really simple... almost as simple as D2W, but more beauty.

Have a model? if so, you can manipulate your db with out writing a single line of code. Try the following:

 * create a Cocoa/EO project importing your model (if you are still in 5.2.1, taking care of recreate the nib file)
 * open your MainMenu.nib (from PB or Xcode) and drag a new window from the IB's palette
 * open your model and drag an entity to the window
 * in IB, press cmd+r

Your table will show up!!

If you are familiar with IB, the reset is history, but in case you are not, you can try:

 * drag a button from the palette to you window
 * connect it (with cntrl+drag) to your display group (it was created in step 3 below) and select the outlet "insert" by double-clicking it
 * press cmd+r
 * push the button

You will see a new row in your table!!

Do you want to add some custom logic?

9. select the Classes tab, select your root class (I prefere NSObject) and press enter 10. press cmd+1, add one outlet (displayGroup) and one action (doit:) 11. press cmd+opt+f (answer ok to the panel) and press cmd+opt+i 12. connect your outlets, add a button and connect it to your action. Save. 13. in PB (or Xcode) you will see your files. Edit them to implement doit:. For example, try something like

- (IBAction)doit:(id)sender{ [[displayGroup displayedObjects] takeValueForKey:aValue :@"aKey"]; }

 * in PB (or Xcode) press cmd+r

You will compile and run your new app. After the table shows up, push the (second) button.

That's it!! You are now ready to develope the best apps in the market ;^

On Memory Management
If you know you are going to require tones of memory, or you get the "java.lang.outOfMemoryError"; message in your console, or your app is simply not doing what it is supposed to, you may need to read this section... otherwise, simply jump it.

Are you still here? well, your problem may be that the VJM did not allocate enough memory for you.

In Project Builder, under targets and with the main target selected, go to Info.plist Entries > Pure-Java Specific (in Xcode, you have to click the little triangle of the targets group, double-click the main target). Add in the "Additional VM Options" text field something like

-Xms256m -Xmx256m

(This will give to your JVM 256Mb of memory instead of 64Mb, the default.)

Also, it is convenient that you get used to use NSAutoreleasePool properly (see its documentation and related topics; in particular, read Chapter 4 of the book "The Objective-C Programming Language" http://developer.apple.com/documentation/Cocoa/Conceptual/ObjectiveC ). The Java Bridge does a good work using "garbage Collection" in the Java side, and "reference count" in the Obj-C side; just do not worry about that.

Morphing Objects
As I said, even if your code uses only Obj-C, you will be dealing with Java objects via the Bridge; therefore you most know how to manipulate those objects "allocated" in the Java side.

The easiest way to allocate an object in the Java side, from the Obj-C one, is, e.g.,

id aJavaObject = [NSClassFromString(@"java.lang.Object") new];

Do not forget to release such an object; since you allocated it, you own it!

For those objects comming from com.apple.cocoa.foundation.* you really does not have to take care (with the remarkable exception of NSEnumerator); they go forth and back nicely. Even do some simple objects like String (which maps to NSString), Number (which maps to NSNumber) and some basic types like int, char, boolean and so.

So far, so good. However, be carefull when you want to enumarate an array. In the one hand, if the array was created in the Obj-C side and you ask for an objectEnumerator, you will recive an NSEnumerator. This object implements the method nextObject to traverse the elements of the array. On the other hand, if the array was created in the Java side (e.g., as a result of an allObjects call) and you ask for an objectEnumerator, you will recive an Enumerator object which implements the nextElement method instead.

Some examples of the two scenarios are the following.

An Obj-C array: NSArray* anObjCArray = [NSArray arrayWithObjects:objA, objB, objC, objD, nil]; NSEnumerator* en = [anObjCArray objectEnumerator]; id o = nil; while(o = [en nextObject]){ // here your code using o,   // which will traverse all objects in the array }

A Java array: NSArray* aJavaArray = [someDisplayGroup displayedObjects]; NSEnumerator* en = [aJavaArray objectEnumerator]; while([en hasMoreObjects]){ id o = [en nextElement]; // here your code using o, which will traverse all objects in the array }

These simple examples show some things to have in mind when developping a CEO:

 * Obj-C's methods may accept arbitrarly large lists of objects as parameters (they usually end with nil); Java's do not. If you know your method will be processed by a Java's object, NEVER use such a construction... it will simply do not be recognized. Instead, Java uses arrays of the form

new Object[] {objA,objB,objC,objD}

Therefore, you most create FIRST the array and, once allocated, use it as argument. An important example is the class method qualifierWithQualifierFromat(String format, NSArray arguments) implemented by com.webobjects.eocontrol.EOQualifier. I'll be back with this isue later...
 * An NSEnumerator ends its "path" with a nil object; Java's Enumerator throws an exception if it is asked for one more element. Therefore, to avoid such an exception, you have to use the hasMoreElements method.
 * Objects in the Java side are called from the Obj-C side as if they were Obj-C objects (with the same sintaxis); that is, if the Java object anObject implements the method someMethod(Object someParameter, Object someOther), you have to call it with a line of the form

[anObject someMethod:someParameter :someOther];

Arrays, NSArrays, and more arrays...
Maybe the worst part of the CEO integration is the managment of arrays; there are 5 types of array you most be aware:

 * NSArray; as defined in Foundation.h (Obj-C side).
 * NSArray; as defined in com.apple.cocoa.foundation (Java side).
 * NSArray; as defined in com.webobjects.foundation (Java side).
 * A C array of objects (like id array[]).
 * A Java array of objects (like Object[]).

This is too much!!!! But life is life, and beter get used to them...

Usually, when you are not intended to use EOF inside Cocoa, you can forget about the array in com.webobjects.foundation; the bridge translates, by default, that of Foundation.h to com.apple.cocoa.foundation, back and forth, with no problem... well, almost: from Java to Obj-C each array go as an NSCFArray which is documented NOWARE, but it works as an NSArray. On the other side, from Obj-C to Java, they go as com.apple.cocoa.NSMutableArray which inherits from the usual NSArray ---try to print their classes description in both sides after they had traveled the bridge with the following code:

[textField setStringValue:aJavaArray class] description;

or

textField.setStringValue(anObjCArray.getClass.toString);

This is not as bad as it may seems. In practice, you do not have to be aware of this.

The real problem start when you want to use EOF. Most of the Enterprise Objects (defined somewhere in com.webobjects.*), when use an array, they are waiting for a com.webobjects.foundation.NSArray and non of the above.

There are many solutions to this problem. The first one (for those who knows Java and Obj-C) is to have a bridgeTool.java object in charge of allocating those arrays as neaded:

public com.webobjects.foundation.NSArray arrayFromArray(com.apple.cocoa.foundation.NSArray cocoaArray) {     com.webobjects.foundation.NSMutableArray woArray = new com.webobjects.foundation.NSMutableArray; int i;     for(i=0;i<cocoaArray.count;++i){ woArray.addObject(cocoaArray.objectAtIndex(i)); }     return woArray; }

And call this method from your Obj-C object with something like

NSArray* cocoaArray; // suppose this exists id bridgeTool = [[NSClassFromString(@"bridgeTool") new] autorelease]; id woArray = [bridgeTool arrayFromArray:cocoaArray];

Once you have your woArray, you can use it as an argument in Enterprise Objects' calls.

I had not foud the way to call a Java method which has an Object[] array as argument. If someone reading this finds out, please let us know...

Building qualifiers
When working with EOF, there are several points when you will want to build a qualifier to perform a fetch. A qualifier is an instance of com.webobjects.eocontrol.EOQualifier. If your qualifier does not include any date, then it is as easy as

NSString* partOfAName; // suppose this exists NSString* qualifierFormat = [NSString stringWithFormat:@"name caseInsensitiveLike '*%@*'", partOfAName]; id qualifier = [NSClassFromString(@"com.webobjects.eocontrol.EOQualifier") qualifierWithQualifierFormat:qualifierFormat :nil];

and then use it in a fetch specification with

id fetchSpecification = [[NSClassFromString(@"com.webobjects.eocontrol.EOFetchSpecification")     new] autorelease];  [fetchSpecification setEntity:@"myEntity"];  [fetchSpecification setQualifier:qualifier];

On the other hand, if you want to use a date in your qualifier, since NSCalendarDate is not properly translated to NSTimestamp, you have to use again the bridgeTool.java trick:

public NSTimestamp nsTimestampFromString(String dateString){ NSTimestampFormatter formatter = new NSTimestampFormatter("%d %m %Y"); ParsePosition pp = new ParsePosition(0); NSTimestamp myNSTimestamp = (NSTimestamp)formatter.parseObject(dateString, pp); return myNSTimestamp; }

and call this from Obj-C by

NSCalendarDate aCalendarDate; // suppose this exists [aCalendarDate setCalendarFormat:@"%d %m %Y"]; id nsTimestamp = [bridgeTool nsTimestampFromString:[aCalendarDate description]];

(just be consistent with your formats).

It is also possible (at least in WO 5.2.4 running on Tiger) to use com.webobjects.eointerface.cocoa.EOCocoaUtilities as follows:

id objPath = @"com.webobjects.eointerface.cocoa.EOCocoaUtilities"; id aDate = [NSCalendarDate date]; id nsTimestamp = [NSClassFromString(objPath) timestampForGregorianDate:aDate];

From here, you can use the nsTimestamp as argument in those classes defined in com.webobjects.* (e.g., in your qualifiers).

Putting things together
Let us start this section describing how to put together two of the most powerful frameworks of OS X; namely, EOF and the NSDocument framework (this section was written, in July 2005, while working in WO 5.2.4 running on Tiger) ---later on, we will integrate these with Core Data which I think that, jointly with Bindings, will substitute EOF in the near future.

Let us do it as a "tutorial":

 * Start Xcode (I am using 2.0) and select File>New Project. In the next panel choose "Cocoa-Java Document-based Application". This generates a project which is ready to use the Java Bridge and implements the NSDocument architecture ---take a look to the generated files and to the target; in particular, double-click on the main target and inspect all details showed.
 * In the project window, select the Frameworks folder. From the Project menu, select Add to Project (cmd+alt+a). In the browser, go to System>Library>Frameworks and select JavaEOCocoa.framework; click OK.
 * Again, press cmd+alt+a, browse to System>Library>Frameworks then to JavaEOAccess.framework>;Resources>Java and select javaeoaccess.jar; click OK.
 * Repeat the previous step for: javaeocontrol.jar, javaeointerface.jar, javaeointerfacecocoa.jar, javafoundation.jar and javaxml.jar.
 * Add your model ---double-check that the folder of it is shown in blue colour (instead of yellow).

Basically you are done. However, if you ---like me--- prefer to work in Objective-C, then do the following:

 * Start Interface Builder by double-clicking the MyDocument.nib file.
 * Click the File's Owner and then the Classes tab. You will notice that the selected class is MyDocument which inherits from NSDocument. Select NSDocument and press "enter" to create a new subclass; give it a name (say, MyCDocument). Then from the Classes menu, select "Create Files..." and accept.
 * Return to the Instances tab, select the File's Owner and in the "Custom class" inspector (cmd+5) select the new created class. Save and hide.
 * Double-click the main target of your project. Select "Document types" and edit accordingly to the new class (e.g., changing MyDocument for MyCDocument).
 * In the .m file created three steps below, add the following:

- (NSString *)windowNibName {   return @"MyDocument"; }

Compile and run (cmd+r).

Finally, if you will, you can get rid of the warnings about deprecated methods in the .java file created by the wizard:

 * From the project window, delete the MyDocument.java file.
 * Check the button "Needs Java" in the "Cocoa Java Specific" pane of your main target ---in the window opened three steps below.

Compile and run (cmd+r).

By this moment, you had created a multi-document CEO...

-- StrauszRicardo


text:
User talk:Spacecoyote

testing... Spacecoyote 14:49, 21 May 2006 (UTC)

title: User talk:Spacecoyote


text:
WebObjects/Web Applications/Development/Component Actions

Overview
The Component Action Request Model is the default means for handling user requests in WebObjects. It is based on the idea of interactive page objects (components).

A page creates some HTML and paints the screen for the user. The component object is stored as part of the user's session, with all of its state. When the user clicks a link or submits a form on the page, the URL has been constructed in such a way that the server can determine which component object was used to create the page originally, and re-uses this object to handle the request.

Thus, you can do quite a good job of forgetting you are on the web, and that the only information the user supplies with each request is the URL, form submission, cookies etc. You can create interactive pages which deal with many requests, simply changing their internal state in response to each one, as though you were writing a non-web application to be used locally.

I'm not familiar myself with JSP, but I'm told by people who are that this makes highly dynamic database editing pages (for example editing several linked tables at once) much easier in WebObjects than JSP. I can confirm this is indeed trivial in WebObjects, although I don't know myself why it is harder in the JSP model.

Note that the Component Action Request Model has some disadvantages. Each URL, to be handled, must find the matching component object in memory. Thus, once the session has timed out, the URL becomes largely incomprehensible and good handling of this situation is difficult. One option is to add additional values as a query string on the URL - this will not break standard request processing but could be used to provide the correct information to recover if the session is gone. Additionally, sessions cannot remember infinite component objects, unless you want to throw memory at the problem, so by default only 30 are stored. This means it is possible for users to backtrack too far and create problems, especially if you are using multiple frames or windows.

For cases where highly interactive pages are not required, you may prefer Direct Actions, which has none of these disadvantages, but provides less help.


text:
WebObjects/Web Applications/Development/Excel Generation

For a "clean" copy of this file, head over to http://www.overhyped.com./downloads/OTCPOIArchive.java

/* Copyright (c) 2003 Overhyped Technologies, LLC. Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction,  including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished   to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ package com.overhyped.webobjects; import com.webobjects.foundation.*; import com.webobjects.appserver.*; import com.webobjects.eocontrol.*; import com.webobjects.eoaccess.*; import com.webobjects.directtoweb.*; import java.io.*; import java.util.Date; import org.apache.poi.hssf.usermodel.*; public class OTCPOIArchive extends Object { protected D2WContext d2wContext; protected EOEnterpriseObject object; static String _fileUploadPath = null; /** Returns the current D2WContext. */   public D2WContext d2wContext { return d2wContext; }   /** Sets the current D2WContext. */   public void setLocalContext(D2WContext d2w) { d2wContext = d2w; }   /** Returns the current EOEnterpriseObject. */   public EOEnterpriseObject object { return object; }   /** Sets the current EOEnterpriseObject. */   public void setObject(EOEnterpriseObject newEO) { object = newEO; }   /** Creates an Excel Spreadsheet using Apache's POI project. If the objectArray is null or empty, this method will fetch all objects from the database for the given entityNameForArchive. If the entityNameForArchive is null or empty, this method will fetch all entities and iterate over them, while fetching all objects for each entityName. This method will use aKey to set an Integer into the target, allowing for feedback while the calculation is occurring. The resulting file can be found in the /excel/ directory of the WebServer. */   public String calculate(NSArray objectArray, String entityNameForArchive, String aKey, WOComponent target) { try { EOEditingContext ec; HSSFWorkbook wb; NSArray entities; java.util.Enumeration entityEnum, attributeEnum, eoEnum; int sCount; String eName; HSSFSheet sheet; NSArray attributes; short col; HSSFRow row; String attribute, attribute2, value; short r;       EOEnterpriseObject item; // set up the d2wcontext if (d2wContext == null) setLocalContext( new D2WContext(target.session) ); // set the task for archive d2wContext.setTask("archive"); // get an EditingContext ec = target.session.defaultEditingContext; // create a workbook wb = new HSSFWorkbook; // get the entities if ((entityNameForArchive == null) || (entityNameForArchive.length == 0)) { entities = D2WUtils.allEntities; objectArray = null; // if there's no entity name, we'd better clear out any objects. }       else { entities = new NSArray(EOUtilities.entityNamed(ec,entityNameForArchive)); }       // create an enumerator entityEnum = entities.objectEnumerator; // count the sheets sCount = 0; // enumerate while (entityEnum.hasMoreElements) { // get the next entity eName = ((EOEntity)entityEnum.nextElement).name; // set the entity into the D2WContext d2wContext.setEntity(EOUtilities.entityNamed(ec, eName)); // get all objects for this entity if ((objectArray == null) || (objectArray.count == 0)) objectArray = EOUtilities.objectsForEntityNamed(ec, eName); // check if there are any objects if ((objectArray == null) || (objectArray.count == 0)) continue; // create a worksheet sheet = wb.createSheet(displayName); // get the propertyKeys for this entity attributes = (NSArray) d2wContext.valueForKey("displayPropertyKeys"); // Label the columns attributeEnum = attributes.objectEnumerator; // zero out the column count col = 0; // Create a row and put some cells in it. Rows are 0 based. row = sheet.createRow((short)0); // iterate the attributes while ( attributeEnum.hasMoreElements ) { // get the next attribute attribute = (String) attributeEnum.nextElement; // set the property into the D2WContext d2wContext.setPropertyKey(attribute); // insert it at the appropriate column // add the cell to the worksheet row.createCell(col).setCellValue(d2wContext.displayNameForProperty); // move to the next column col++; }       // Insert the Rows // make sure we start at row 1, otherwise we'll lose the column headings r = 1; // set up to iterate the EOs eoEnum = objectArray.objectEnumerator; // iterate the EOs while ( eoEnum.hasMoreElements ) { try { // zero out the column count col = 0; // create a new row row = sheet.createRow(r); // an EO           item = (EOEnterpriseObject) eoEnum.nextElement; // set it into the D2W page setObject(item); // set up to iterate the attributes again... attributeEnum = attributes.objectEnumerator; // iterate the attributes again... while ( attributeEnum.hasMoreElements ) { // the attribute attribute2 = (String) attributeEnum.nextElement; // set the property into the D2WContext d2wContext.setPropertyKey(attribute2); try { // the final value to insert from the current object based on the d2wcontext value = currentValue; // create the cell for the spreadsheet row.createCell(col).setCellValue(value); }               catch (Exception e1) { System.err.println(e1); }               // move over a column col++; }           }            catch (Exception e2) { System.err.println(e2); }           // move down a row, since there's a new object r++; target.takeValueForKey(new Integer(r),aKey); }       // add another worksheet to the spreadsheet, since there's another entity sCount++; // clear out the objectArray, since there's another entity objectArray = null; // System.out.println("POIArchive: calculate: Completed Archive: " + eName); }       // All sheets and cells added. Now write out the workbook String fn = fileUploadPath + File.separator + "POI" + new NSTimestamp.getTime + ".xls"; File file = new File(fn); FileOutputStream fileOut = new FileOutputStream(file); wb.write(fileOut); fileOut.close; objectArray = null; entityNameForArchive = null; System.out.println("POI @ " + fn); return fn; }   catch (Exception e) { ByteArrayOutputStream baos = new ByteArrayOutputStream; e.printStackTrace(new PrintStream(baos)); System.out.println("POIArchive: archive: error:" + baos.toString); return null; }     }    /** Returns the current value provided by the d2wContext. Found objects use the d2wContext for timestamp formatting, relationships, etc. If a relationship fo an EOEnterpriseObject is resolved, and no keyForRelationship is found, this method will use the EO's userPresentableDescription. */   public String currentValue { Object o = null; try { if (d2wContext.propertyKeyIsKeyPath) { o = NSKeyValueCodingAdditions.Utility.valueForKeyPath(object, d2wContext.propertyKey); } else { o = NSKeyValueCoding.DefaultImplementation.valueForKey(object, d2wContext.propertyKey); }       if ( (o != null) && (o instanceof EOEnterpriseObject) ) { o = ((EOEnterpriseObject) o).valueForKey(d2wContext.keyWhenRelationship); if ( (o != null) && (o instanceof EOEnterpriseObject) ) { try { return ((EOEnterpriseObject)o).userPresentableDescription; }           catch (Exception ex) { ByteArrayOutputStream baos = new ByteArrayOutputStream; ex.printStackTrace(new PrintStream(baos)); System.out.println("POIArchive: currentValue: error" + baos.toString); return o.toString; }       }        }        if ( (o != null) && (o instanceof NSArray) ) { /***** ENHANCE ME: could use componentsSeparatedByString along with userPresentableDescription. jpaul. *****/       int count; count = ((NSArray) o).count; if (count == 0) return "0"; return " " + count; }       if ( (o != null) && (o instanceof NSTimestamp) ) { NSTimestampFormatter formatter = new NSTimestampFormatter((String) d2wContext.valueForKey(D2WModel.FormatterKey)); return formatter.format((NSTimestamp) o); }       if ( (o != null) && (o instanceof java.lang.Number) ) { NSNumberFormatter formatter; String pattern = (String) d2wContext.valueForKey(D2WModel.FormatterKey); if ((pattern != null) && (pattern.length > 0)) { formatter = new NSNumberFormatter(pattern); }       else { formatter = new NSNumberFormatter; }       return formatter.stringForObjectValue(o); }       if (o != null) return o.toString; return ""; }   catch (Exception e) { return "(Binary Data)"; }   }    /** Returns an EOEnterpriseObject's 'displayNameForEntity', using the d2wContext. If it is not found, the method will check the userInfoDictionary for displayName. If all else fails, it will return the entity's name. */   public String displayName { String s;   s = (String) d2wContext.valueForKey("displayNameForEntity"); if (s == null) { s = (String) d2wContext.entity.userInfo.objectForKey("displayName"); if (s != null) return s;       return (String) d2wContext.entity.name; }   return s;    } /**   * Returns the directory where uploaded files will be written to disk. */   public static String fileUploadPath { if (_fileUploadPath == null) { String tmpdir = System.getProperty("OTCUploadDirectory"); if (tmpdir != null) { File tmpPath = new File(tmpdir); if (tmpPath.exists) { _fileUploadPath = tmpPath.getAbsolutePath; }       }        if (_fileUploadPath == null) { NSLog.err.appendln("FileUpload: 'OTCUploadDirectory' does not exist. Please launch this application again with the 'OTCUploadDirectory' System Property set to a directory to which you have write permission."); }   }    return _fileUploadPath; } }


text:
WebObjects/EOF/Using EOF/Database Adaptors and Plugins

WebObjects Wikibooks considered deprecated
The WebObjects Wikibooks pages are considered deprecated. Please find this page on the new wiki at http://wiki.objectstyle.org/confluence/display/WO/EOF-Using+EOF-Database+Adaptors+and+Plugins.

PostgreSQL
These instructions are especially for those wanting to use PostgreSQL with primary keys of type serial.

(Valid as of 5th May 2005 for a Mac OS X development system with WO5.2.3 and PostgreSQL 7 or 8)

Step 1
Obtain the following items from http://www.hexdreams.com/ placing their built products as suggested (but do check each item for bug fixes or notes)...

 * PostgresqlPlugIn.framework --> /Library/Frameworks
 * PostgresqlPlugInBundle.EOMPlugin --> /Developer/EOMBundles

Step 2
You should have the following Adaptor info set (in EOModeler) for your database after the above install:
 * Username: username
 * Password: password
 * URL: jdbc:postgresql://localhost/yourDb
 * Driver: org.postgresql.Driver
 * Plugin: PostgresqlPlugIn(*)

(*) Note: case sensitive.

Step 3
Link the PostgresqlPlugIn.framework into your project.

MySQL
From: http://www.macdevcenter.com/pub/a/mac/2003/09/26/webobjects.html?page=3

''In order to set up MySQL you will need to install it first. If you have not, I recommend using Aaron Faby's fantastic installer (http://www.serverlogistics.com). After installing MySQL you will need the correct JDBC driver, which can be downloaded from the MySQL website. (put it into /Library/Java/Extensions/)

From there, you will need to do the following, using the "Model:Switch Adaptor..." menu item:
 * Use the JDBC connector
 * Username:
 * Password:
 * URL: jdbc:mysql://localhost/
 * Driver (optional): com.mysql.jdbc.Driver (Note: in old MySQL versions it was org.gjt.mm.mysql.Driver)''
 * PlugIn (optional): leave blank

Also see: http://web.archive.org/20040710072001/homepage.mac.com/kelleherk/iblog/C711669388/E733468496/index.html and: http://web.archive.org/20041122232437/homepage.mac.com/kelleherk/iblog/C711669388/E1395049038/index.html

OpenBase
The OpenBase installer should install the JDBC driver. In EOModeller set the URL to:

 * jdbc:openbase:// /

The Driver should be set to:

 * com.openbase.jdbc.ObDriver

Plugin should be left blank.

FrontBase
The OpenBase installer should install the JDBC driver. In EOModeller set the URL to:

jdbc:FrontBase://127.0.0.1/Tempo

Driver and plugin should be left blank.

MSSQLServer
You will need the JDBC driver for SQL 2000 from Microsoft. A search for Microsoft SQL Server 2000 JDBC should give you the right link. You can untar the file and place the resulting .jar files in /Library/Java/Extensions.

In EOModeler use the following settings:

 * UserName:
 * Password:
 * URL: jdbc:microsoft:sqlserver://:1433;DatabaseName=<;DB name>
 * Driver: com.microsoft.jdbc.sqlserver.SQLServerDriver
 * PlugIn: MicrosoftPlugIn

If you neglect to specify the MicrosoftPlugIn it will let you connect to the database, but will show you tables that appear to be meta data instead of your actual data.

FileMaker
360works makes a PlugIn for FileMaker, which is now an open source project.


text:
WebObjects/EOF/Using EOF/Common Pitfalls and Troubleshooting

Deadlocks
The most common cause of this is EOEditingContextLock but you can also cause problems by leaving locks on the EODatabaseContext. To find out where you are locking improperly you will need to start tracking editing context locks. Review the Context and Database Locking section for more information.

Subclass Missing Primary Key
If you are subclassing across EO models and EOF is not generating a primary key in SQL when you insert your subclass (the result would be an integrity violation for a null primary key), then you have likely run into the annoying behavior of EOF when dealing with models that have SLIGHTLY different connection dictionaries. Basically, EOF decides when to share EODatabases across multiple models by comparing the connection dictionaries. However, gggthe connection dictionary contains a large block named jdbc2Info with cached type and driver information. If you happened to create your models with, for instance, different jdbc driver versions, that version number will cause the dictionaries to not be equals. When this occurs, EOF assigns each model to its own EODatabase, which causes SQL generation for the subclass entity to enter a codepath that ends up not generating a primary key. Chuck Hill recommends removing jdbc2Info at runtime to prevent problems like this from occurring.

No Suitable Driver Found
If you get the following exception:

com.webobjects.jdbcadaptor.JDBCAdaptorException: No suitable driver at com.webobjects.jdbcadaptor.JDBCContext.connect(JDBCContext.java:244) at com.webobjects.jdbcadaptor.JDBCContext._tryConnect(JDBCContext.java:331) at com.webobjects.jdbcadaptor.JDBCContext._channelWillOpen(JDBCContext.java:468) at com.webobjects.jdbcadaptor.JDBCChannel.openChannel(JDBCChannel.java:81)

Odds are very good that your classpath is incorrect. There are two parts to database support in EOF, the JDBC driver, and the Database Plugin. You must make sure that BOTH are in your classpath. For instance, on FrontBase, you must have both frontbasejdbc.jar AND frontbaseplugin.jar (from the FrontBasePlugin.framework) in your classpath.


text:
WebObjects/Web Applications/Development/SSL

Detecting SSL
Code for detecting whether SSL is active for the current request: I'm told this won't work with IIS:

// Is this page being accessed securely? boolean secureMode = false; String header = context.request.headerForKey("https"); if( header == null ) { log.debug( "no https header, looking for server_port" ); header = context.request.headerForKey( "server_port" ); if( header == null ) { log.debug( "no server_port header found, assuming insecure connection" ); } else { log.debug( "server_port header found, using it" ); secureMode = header.equals( "443" ); } } else { log.debug( "https header found, using it" ); secureMode = header.equals( "on" ); } log.debug( "secure mode set to " + secureMode );


text:
WebObjects/Web Applications/Development/Localization and Internationalization

Unicode
To Enable Unicode for your WO app, add the following to your application constructor:

WOMessage.setDefaultEncoding("UTF8");

This tells all WOResponse and WORequest to use UTF8 (Unicode).

Then you just need to tell the browser. Make all your .wo pages include this meta tag in their HTML:

Jesse Barnum
Great tip - here is a simple method call you can stick in your Application object to automatically achieve the results outlined above:

private boolean enableUTFEncoding = false; public void enableUTFEncoding { enableUTFEncoding = true; WOMessage.setDefaultEncoding(_NSUtilities.UTF8StringEncoding); } public WOResponse dispatchRequest(WORequest theRequest) { WOResponse result = super.dispatchRequest(theRequest); if( enableUTFEncoding && "text/html".equals(result.headerForKey("content-type")) ) { result.setHeader("text/html; charset=UTF-8; encoding=UTF-8", "content-type"); }   return result; }

Helmut Schottmüller
Unfortunately it's not so easy if you want to use file upload fields and UTF-8 encoding in the same form. Adding a file upload component means that you have to set the form's enctype to "multipart/form-data". To force a multipart form to use UTF-8 encoding usually needs an enctype of "multipart/form-data; charset=UTF-8" but WO is not able to identify such a form as multipart form. You will get a "java.lang.IllegalArgumentException: This form is missing a 'enctype=multipart/form-data' attribute. It is required for WOFileUpload to work." error when you open the form in the browser.

To make sure that UTF-8 is supported in multipart forms as well, you have to add the following code to your Application object:

public WORequest createRequest(String aMethod, String aURL, String anHTTPVersion, NSDictionary someHeaders, NSData aContent, NSDictionary someInfo) {   WORequest newRequest = super.createRequest(aMethod, aURL, anHTTPVersion, someHeaders, aContent, someInfo); newRequest.setDefaultFormValueEncoding(_NSUtilities.UTF8StringEncoding); return newRequest; }

To make WOFileUpload components working I also had to add the launch parameter -WOUseLegacyMultipartParser true to my application. This launch parameter forces the parsing of all form values, the first time WORequest.formValues is called. See the apple developer documentation for additional information. Without -WOUseLegacyMultipartParser true I had serious problems in my applications using a WOFileUpload component because the bindings data and filePath have been emptied after a form POST.

With Jesse's code and this extension, you will be able to handle UTF-8 character data correctly in your WO application.

If you use localized strings in your UTF-8 application you may also check out Project Wonder's ERXLocalizer class.


text:
WebObjects/EOF/Using EOF/Batch Fetching

Petite Abeille
Here is a little example about how to batch fetch a bunch of EOFaults:

without triggering the fault: EOEnterpriseObject		aReferenceObject = (EOEnterpriseObject)someFaults.lastObject; EOAccessFaultHandler	aFaultHandler = (EOAccessFaultHandler) EOFaultHandler.handlerForFault(aReferenceObject); EOEditingContext		anEditingContext = aFaultHandler.editingContext; String				anEntityName = aFaultHandler.globalID.entityName; EOEntity				anEntity = EOModelGroup.defaultGroup.entityNamed( anEntityName );
 * First, given an array of faults, how to retrieve meta information

EOEnterpriseObject	aFault = (EOEnterpriseObject) someFaults.objectAtIndex(index); EOKeyGlobalID		aGlobalID = (EOKeyGlobalID) anEditingContext.globalIDForObject( aFault ); NSDictionary		aPrimaryKey = anEntity.primaryKeyForGlobalID( aGlobalID ); EOQualifier		aQualifier = anEntity.qualifierForPrimaryKey( aPrimaryKey );
 * Second, for each EOFault build a qualifier:

EOQualifier			aQualifier = new EOOrQualifier( someQualifiers ); EOFetchSpecification	aFetchSpecification = new EOFetchSpecification( anEntityName, aQualifier, null); anEditingContext.objectsWithFetchSpecification?(aFetchSpecification?);
 * Finally, resolve the qualifier:

 * Attached is the entire method.

// =========================================================================== //	Batch fetching Class method(s) // --- public static void batchFetchObjects(NSArray someObjects) {   if ( ( someObjects != null ) && ( someObjects.count > 0 ) ) {     NSMutableArray  someFaults = new NSMutableArray; int   count = someObjects.count; for ( int index = 0; index < count; index++ ) {       Object  anObject = someObjects.objectAtIndex(index); if ( EOFaultHandler.isFault( anObject ) == true ) {         someFaults.addObject( anObject ); }     }      if ( someFaults.count > 0 ) {       NSMutableArray    someQualifiers = new NSMutableArray; EOEnterpriseObject aReferenceObject = (EOEnterpriseObject) someFaults.lastObject; EOAccessFaultHandler aFaultHandler = (EOAccessFaultHandler) EOFaultHandler.handlerForFault(aReferenceObject); EOEditingContext anEditingContext = aFaultHandler.editingContext; String     anEntityName = aFaultHandler.globalID.entityName; EOEntity   anEntity = EOModelGroup.defaultGroup.entityNamed( anEntityName ); count = someFaults.count; for ( int index = 0; index < count; index++ ) {         EOEnterpriseObject  aFault = (EOEnterpriseObject) someFaults.objectAtIndex(index); EOKeyGlobalID   aGlobalID = (EOKeyGlobalID) anEditingContext.globalIDForObject( aFault ); NSDictionary   aPrimaryKey = anEntity.primaryKeyForGlobalID( aGlobalID ); EOQualifier   aQualifier = anEntity.qualifierForPrimaryKey( aPrimaryKey ); someQualifiers.addObject( aQualifier ); }       if ( someQualifiers.count > 0 ) {         EOQualifier    aQualifier = new EOOrQualifier( someQualifiers ); EOFetchSpecification aFetchSpecification = new EOFetchSpecification( anEntityName, aQualifier, null); anEditingContext.objectsWithFetchSpecification(aFetchSpecification); }     }    }  }

Jonathan Rochkind
''Can you explain the advantages of fetching faults instead of just letting the editingContext return them? -SamBarnum''

A 'fault' will not actually be fetched until it is asked for. It's just an object in memory that says 'if anyone tries to access my data, I will fetch it from the db.' You usually don't run into a situation where you want to create faults instead of just fetching objects from the EC---but when you're doing complicated programming, it will be obvious when you do. I think just once have I actually manually created faults. [When I had a semantic relationship, but couldn't actually model the relationship, and implemented a sort of half baked relationship scheme of my own instead.]

However, I'm not sure why you'd want to use the above code when the EOEditingContext method faultForRawRow is available. Make an NSDictionary which has pk attribute names as keys and desired values as their values, pass it to yourEC.faultForRawRow?, you've got a fault. A heck of a lot easier than the above.

Preloading Relationships
This method can be used to prefetch relationships in a batch instead of having them fire one by one. This can be used to fine tune batch fetching in code instead of doing it in the model. Be careful of how you use this method. It will force a fetch from the database even if the objects are already cached in the object store. Used incorrectly this can reduce, instead of increase, performance.

public static void preloadRelationship(NSArray sourceEOs, String relationshipName) { if (sourceEOs.count != 0) { EOEnterpriseObject sampleEO = (EOEnterpriseObject) sourceEOs.objectAtIndex(0); EOEditingContext ec = sampleEO.editingContext; EOObjectStoreCoordinator osc = (EOObjectStoreCoordinator) ec.rootObjectStore; osc.lock; try { EOEntity entity = EOModelGroup.modelGroupForObjectStoreCoordinator(osc) .entityNamed(sampleEO.entityName); EODatabaseContext databaseContext = EODatabaseContext. registeredDatabaseContextForModel(entity.model, osc); EORelationship relationship = entity.relationshipNamed(relationshipName); databaseContext.batchFetchRelationship(relationship, sourceEOs, ec); }        finally { osc.unlock; }    }  }

title: WebObjects/EOF/Using EOF/Batch Fetching


text:
Bartending/Cocktails/Flaming Dr. Pepper

The Flaming Dr. Pepper is a cocktail with a taste reminiscent of the commercial soft drink 'Dr. Pepper'.

Ingredients

 * 3/4 shot Amaretto
 * 1/4 shot Bacardi 151 or Everclear
 * 1/2 pint Beer

Glass

 * Shot glass
 * Pint glass

Instructions
In a single shot glass, delicately pour the 1/4 shot of Bacardi on top of the 3/4 shot of Amaretto being careful to avoid mixing the 2 ingredients together. When the consumer is ready, use a match to ignite the Bacardi in the shot glass and drop the shot into the half pint of beer. The drink should be quickly consumed immediately following the dropping of the shot into the beer.

Related Cocktails

 * Numerous alcoholic beverages whose flavour resembles that of soft drinks exist. For example the Cheeky Vimto

Flaming Dr. Pepper

title: Bartending/Cocktails/Flaming Dr. Pepper


text:
WebObjects/Web Applications/Development/Common Pitfalls and Troubleshooting

Chuck Hill
I recently found this useful and thought others might too:

System.out.println("\nClassPath Follows....\n"); ClassLoader classLoader = ClassLoader.getSystemClassLoader; URL[] urls = ((URLClassLoader)classLoader).getURLs; for(int i=0; i<urls.length; i++) {   System.out.println(urls[i].toString + "\n"); }

Add this to public static void main(String argv[]) before the call to WOApplication.main(argv, Application.class) and add these imports:

import java.io.*; import java.net.*;


text:
Suomen kieli käyttöön/Euroviisuvoitto

Finland's monster rock band Lordi won the 2006 Eurovision Song Contest which was staged in Athens. This was how the win was summarised by broadcaster YLE which now has the incredibly complex and expensive task of hosting this mammoth competition in 2007.

Basic Text
Suomen edustaja Lordi on voittanut Euroviisut kisojen kaikkien aikojen korkeimmalla pistesaaliilla 292. Toiseksi tuli Venäjä 248 ja kolmanneksi Bosnia-Hertsegovina 229 pisteellä.

Lordi keräsi täydet 12 pistettä kaikista pohjoismaista, sekä Virosta, Puolasta, Britanniasta ja Kreikasta. Voitto tarkoittaa myös sitä, että ensi vuonna Euroviisut järjestää Suomi. Lordi on kiittänyt rockfaneja eri puolilla Eurooppaa. Lordin mukaan voitto on osoitus siitä, että euroviisuihin sopii monenlainen musiikki.

Text with English translation
'''Suomen edustaja Lordi on voittanut Euroviisut kisojen kaikkien aikojen korkeimmalla pistesaaliilla 292. '''
 * The Finnish representative Lordi has won the Eurovision competition by bagging an all-time highest points score of 292.

'''Toiseksi tuli Venäjä 248, ja kolmanneksi Bosnia-Hertsegovina 229 pisteellä. '''
 * Second came Russia 248 and third Bosnia-Hertzegovina with 229 points.

Lordi keräsi täydet 12 pistettä kaikista pohjoismaista, sekä Virosta, Puolasta, Britanniasta ja Kreikasta.
 * Lordi collected the full 12 points from all the Nordic countries, from Estonia, from Poland, from Great Britain, and from Greece.

'''Voitto tarkoittaa myös sitä, että ensi vuonna Euroviisut järjestää Suomi. '''
 * The win also means that next year Finland will organize Eurovision.

'''Lordi on kiittänyt rockfaneja eri puolilla Eurooppaa. '''
 * Lordi has thanked rock fans from all over Europe

Lordin mukaan voitto on osoitus siitä, että euroviisuihin sopii monenlainen musiikki.
 * According to Lordi, the win indicates that Eurovision is suitable for many kinds of music.

title: Suomen kieli käyttöön/Euroviisuvoitto


text:
WebObjects/EOF/Using EOF/Configuration Settings

(true for WO5) turns on Adaptor logging. This gives you the SQL statements that are issued against your database.
 * -EOAdaptorDebugEnabled YES/NO

(false for WO5) (this one is quite hidden in the docs) Turns off bind variables on the SQL statements, making SQL much more readable when debugging.
 * -EOAdaptorUseBindVariables YES/NO


text:
WebObjects/EOF/Modeling/EOModeler

Value type documentation
Yes it exists and it used to here (the link is now dead): http://developer.apple.com/documentation/WebObjects/UsingEOModeler/4WorkingWithAttributes/chapter_4_section_3.html#//apple_ref/doc/uid/DontLinkBookID_504-DontLinkChapterID_3-BABFGECE

Class generation
This is one of the most vexed and difficult area. The main issue is the separation of generated code from your own business logic.

Here are some ways of dealing with this issue:

Apple suggested way
This is pretty dangerous as you will need to do a merge each time you do some modification to the model. I have never managed to make it work without a lot of sweat.

Inheritence
This is the method I use most of the time. For each real entity you create two entities in the model: one that will contain the business logic and one that will contain all the accessors. The accessor entity is an abstract entity an the pare n t of the real entity. The real entity is the one that you application will interact with and will never need to be regenerated. The abstract entity is used to carry all the accessor and you can regenerate it whenever it is needed as it does carry any of y our code.

All relationship must be established with the real entity or you will have problems

''Just one more trick with this solution try to keep all the abstract entities together by prefixing them with a com mon pre fix. That way you can select all of them and regenerate them all with one command (nearly you still need to acknowledge the overwrite confirmation).''

EOGenerator
This tool makes the inheritance trick transparent. For more information, read the EOGenerator section.

?
Although using EOGenerator or the other method above to model your EOs (using the GenerationGap design pattern) is helpful when working with EOModeler, it also means that you have double the number of EO classes to deal with, and more messaging up the inheritance hierarchy at runtime, and more files to check in/out of CVS, etc.

What would be nice would be if EOGenerator had the ability to 'collapse' the 2 related EO classes together once you decide that your model is relatively stable, and you won't be needing to make a lot of EOModel changes any more. Early on in the project lifecycle, when more changes are made, EOGenerator can be very useful.

Jesse Barnum
What about not generating class files from EOModeler at all (assuming you're subclassing EOGenericRecord), and leaving the EO classes empty except for custom methods, and accessors that you specifically manually add? This should work fine as long as you use KVC to get your object properties. Is there a downside to this that I'm not seeing?

By hand
Just add/remove/edit the attributes and relationships by hand.

Adding Referential Integrity Rules
You can use the Advanced Relationship Inspector to add referential integrity rules for a relationship.

To add referential integrity rules:

 * Select the relationship for which you want to add rules.
 * In the Relationship Inspector, click the Advanced Relationship Inspector icon.

You can use the fields in the Advanced Relationship Inspector to further specify a relationship. The options in this inspector are described in the following sections.

Batch Faulting
Normally when a fault is triggered, just that object (or array of objects for a to-many relationship) is fetched from the database. You can take advantage of this expensive round trip to the database by batching faults together. The value you type in the Batch Size field indicates the number of faults for the same relationship that should be triggered along with the first fault. For more discussion of batch faulting, see the class specification for EODatabaseContext in the Enterprise Objects Framework Reference.

Optionality
This field lets you specify whether a relationship is optional or mandatory. For example, you could require all departments to have a location (mandatory), but not require every employee to have a manager (optional).

Delete Rule
This field lets you specify the delete rules that should be applied to an entity that's involved in a relationship. For example, you could have a department with multiple employees. When a user tried to delete the department, you could:
 * Delete the department and remove any back reference the employee has to the department (Nullify).
 * Delete the department and all of the employees it contains (Cascade).
 * Refuse the deletion if the department contains employees (Deny).
 * Allow the deletion and do nothing to the destination objects (No Action).

The No Action rule is useful for tuning performance. However, you should use this delete rule with great caution since it can result in dangling references in your object graph. For more information, see the class specification for EOClassDescription in the Enterprise Objects Framework Reference.

Owns Destination
The Owns Destination checkbox lets you set a source object as owning its destination objects. When a source object owns its destination objects and you remove a destination object from the source object's relationship array, this also has the effect of deleting it from the database (alternatively, you can transfer it to a new owner). This is because ownership implies that the owned object can't exist without an owner-for example, line items can't exist outside of a purchase order.

Propagate Primary Key
The Propagate Primary Key checkbox lets you specify that the primary key of the source entity should be propagated to newly inserted objects in the destination of the relationship. This is typically used for an owning relationship, where the owned object has the same primary key as the source. For example, in the Movies database the TalentPhoto entity has the same primary key as the entity that owns it, Talent.

EOModeler Bugs
EOModeler is a very old application that has not gotten much attention from Apple since the NeXT purchase. There are quite a few odd behaviors, some of which are documented on the EOModeler Bugs page.

For EOModeler bugs related to Prototypes, see Prototype Gotchas.

title: WebObjects/EOF/Modeling/EOModeler


text:
Bartending/Cocktails/Cheeky Vimto

The Cheeky Vimto is a cocktail whose flavour resembles that of the soft drink 'Vimto'. The soft drink is made by Vimto Soft Drinks Ltd, a division of Nichols plc.

Ingredients

 * 2 shots of Port
 * 1 bottle (330ml) of WKD Blue

Glass

 * Pint

Instructions
Put the double shot of port into the pint glass and add the WKD Blue.

Related Cocktails

 * Numerous cocktails are available whose flavour resembles that of commercial soft drinks. For example the Flaming Dr. Pepper.

title: Bartending/Cocktails/Cheeky Vimto


text:
WebObjects/EOF/Modeling/EOModeler Bugs

 * How can a property editor be so buggy?
 * This distracts from the power of WebObjects

---

What kind of bugs are you experiencing? Could you be specific?

The first impression of EOModeler is that we could do a lot with it and it usually does at first. The problems start when the model becomes more complex. The merge feature is somewhat nice but we need a more robust mechanism for separating generated code and custom one.
 * Model editing bugs
 * If you are using inheritance, and most real life application do, EOModeler exhibits some very strange behavior.
 * If you copy and paste an Attribute that is defined by a prototype it will some times not copy it or copy partial information in the children especially if you have overwritten some of the attribute of the prototype. (It loses the primary key everytime)
 * Copy and paste of relationship in parent entities is not reflected in the children.
 * With some deep inheritance turning the attribute bit on or off is not always propagated to the children.
 * Project Builder integration
 * When you generate a new java file it brings a dialog box asking for insertion in the project but answering OK does nothing.
 * Run time bugs
 * The one that I know about and still bites me regularly, although fortunately not too often, is the internalInfo one. EOModeler inserts in the model file some properties that are not used at run time. Sometimes these properties will cause a crash i.e. in a prototype entity or on relationship.

---

Newbie says:

I'm seeing a bug when trying to syncronize the EOModeler file with my mySQL database. I suspect that it's a problem with the adaptor, but I'm not sure. It's giving me an alert box: "ALERT : no reason provided". How does one debug the adaptor's operation? I couldn't find any information about this bug, searching omni nor apple.

I'm not sure that feature has ever worked since v5 need to synchronize manually.

The "ALERT : no reason provided" bug may be because your database has tables that consist only of primary keys (e.g., tables used to create many-to-many relationships). EOF's reverse engineering code chokes when it encounters a table that will have no class properties.

Another bug. I submitted this one to Apples Bugreporter

In WebObjects EOModeler I try to setup a many-to-many relationship as described in the EOModeler documentation. This leads to a problem, where I can't generate SQL Statements from my entities. There is an error message displayed that says "*** -[NSCFDictionary? setObject:forKey:]: attempt to insert nil value"

 * STEPS TO REPRODUCE
 * 1) Open EOModeler
 * 2) Setup new model as described in WebApplications? documentation (look into http://developer.apple.com/documentation/WebObjects/Web_Applications/EnterpriseObjectsApp/chapter_11_section_3.html#//apple_ref/doc/uid/TP30000121/BAJEFFEH)
 * 3) Generate a second entity Book in the same way as described above. Let it have just one attribute "title" and a primary key "bookID"?;. Same properties as the entity "Author"
 * 4) Try to setup a many-to-many relationship as described in "Using EOModeler"?; documentation (look into http://developer.apple.com/documentation/WebObjects/UsingEOModeler/5WorkingWithRelationships/chapter_5_section_7.html#//apple_ref/doc/uid/DontLinkBookID_504-DontLinkChapterID_4-BABGHHGG)
 * 5) Switch to diagram view
 * 6) Select both entities
 * 7) Select "Join in many-to-many" from "Property" menu
 * 8) Give the newly created entity a tablename (f.e. "AUTHOR_BOOK"?;)
 * 9) Check Consistency from the Model menu (all well)
 * 10) Try to generate SQL commands for the creation of the tables for your model by selecting the first entity "Author" and then "Generate SQL..."?; from the "Property" menu.
 * 11) Boom! Error message.

I expected the command to flawlessly function and generate SQL statements so that I can create my tables and use my model.
 * RESULTS

Tried different project setups, isolated to minimal count of tables possible. Reread documentation. No solution.
 * REGRESSION

title: WebObjects/EOF/Modeling/EOModeler Bugs


text:
WebObjects/Web Applications/Development/Custom Error Handling

Exception Page
To provide a custom error handler when an exception is thrown, override the method:

public WOResponse handleException (java.lang.Exception anException, WOContext aContext) { }

For example:

public WOResponse handleException(Exception _exception, WOContext _context) { Session session = (Session) _context.session; // do something to notify user of exception, maybe put a message in the session here ... WOResponse response; if (/* you can't handle exception*/) { response = super.handleException(_exception, _context); }   else { response = pageWithName(YourExceptionPage.class.getName, _context).generateResponse; }   return response; }

Session Expired
To provide a custom error handler when an exception is thrown, override the method:

public WOResponse handleSessionRestorationErrorInContext(WOContext _context) {

For example: public WOResponse handleSessionRestorationErrorInContext(WOContext _context) { Session session = (Session) _context.session; // do something to notify user of exception, maybe put a message in the session here ... WOResponse response = pageWithName(YourErrorPage.class.getName, _context).generateResponse; return response; }

Chuck Hill
This message started to appear in WO 5.2 when an exception was raised in a DirectAction, but only in deployed applications. Here is what is happening:

WODisplayExceptionPages true or false to enable or disable the generation of WOExceptionPages for direct action requests. Default is true in development mode and false in deployment mode.

From http://developer.apple.com/documentation/WebObjects/WOAppProperties/AppProperties/chapter_1_section_1.html

If the page being returned throws during appendToResponse and the app is deployed, all that is displayed is a blank page with the words "Your request produced an error".

To avoid this, either add this to the launch agruments: -DWODisplayExceptionPages=true

Or change the main method in your Application class to look like this:

public static void main(String argv[]) { System.setProperty("WODisplayExceptionPages", "true"); WOApplication.main(argv, Application.class); }

This won't work if you put it in the constructor.