Showing posts for tag "sntt"

SNTT(uesday): Stepping Up My Tycho Game

Jan 15, 2019 11:21 AM

Tags: sntt tycho

I'm always on the lookout for ways to improve my projects' build process to get more-convenient results, cut down on IDE/compiler complaints, or to generally reduce the amount of manual work.

In the last couple weeks, I've figured out two changes to make that clean up my setup nicely: better source bundles and easier update sites.

Source Bundles

In OSGi parlance, a "source bundle" is a companion bundle/plugin for a normal bundle that contains the source code associated with it - for example, org.openntf.domino is paired with org.openntf.domino.source. With a bundle like this present, an IDE (Designer included) can pick up on the presence of the source code and use it for Javadoc and showing the original source of a class. It's extraordinarily convenient, rather than having to reference the source online or in another project (or not at all).

For a while, I've configured my Tycho projects to automatically generate these source bundles during build, and then I have ".source" features that reference them, which are then included in the final update site. This works very well, but it leaves the nagging problem that Eclipse complains about not being able to find the auto-vivified source bundles, and it also requires either putting the source bundles in the main features (which is a bit inefficient in e.g. a server deployment) or maintaining a separate ".source" feature.

It turns out that the answer has been in Tycho all along: instead of just generating source bundles, you can tell it to generate entire source features on the fly. You can do this by using the aptly-named tycho-source-feature-plugin:

<plugin>
	<groupId>org.eclipse.tycho.extras</groupId>
	<artifactId>tycho-source-feature-plugin</artifactId>
	<version>${tycho-version}</version>
	<executions>
		<execution>
			<id>source-feature</id>
			<phase>package</phase>
			<goals>
				<goal>source-feature</goal>
			</goals>
		</execution>
	</executions>
	<configuration>
		<includeBinaryFeature>false</includeBinaryFeature>
	</configuration>
</plugin>

With this, the build will auto-create the features as it goes, including pulling in the source of any referenced third-party bundles, and then you can include them in the final update site. For example, if the feature you're building is com.example.foo.feature, you can include com.example.foo.feature.source in your output.

Eclipse Repositories

Historically, the way Domino-targeted update sites are built is that they're referred to as the project type eclipse-update-site, which takes a site.xml and turns it into the final update site. This works well enough, but it has a couple problems. For one, it's deprecated and ostensibly slated for removal down the line, and it's best to not rely on anything like that. But otherwise, even when it works, it's fiddly: if you want to, for example, bring in a third-party feature, you have to explicitly specify the version of the feature you're bringing in, rather than letting the build environment pick up on what it is. This can turn into a drag over time, and it's always felt like unnecessary maintenance.

The immediate replacement for eclipse-update-site is eclipse-repository, which is very similar (you can "convert" by just changing the project type and renaming site.xml to category.xml) and solves the second problem. In a category.xml file, you can specify just the feature ID, leaving the version out or specified as 0.0.0, and it'll figure it out during the build.

However, this has a minor down side: though Designer can deal with these repositories without issue, the NSF Update Site template doesn't know about the generated artifacts.jar and content.jar files. You can use "Import Features", but that loses the feature categories, which are very useful when maintaining a large update site.

Fortunately, the site.xml format is extremely basic, so I created a Maven plugin a while ago to auto-generate one of these files. I improved it yesterday to pick up on the categories specified in the original category.xml file. This let me tweak the eclipse-repository project to shim in this generation before the final packaging:

<build>
	<plugins>
		<plugin>
			<groupId>org.darwino</groupId>
			<artifactId>p2sitexml-maven-plugin</artifactId>
			<version>1.1.0</version>
			<executions>
				<execution>
					<id>generate-sitexml</id>
					<goals>
						<goal>generate-site-xml</goal>
					</goals>
					<phase>package</phase>
				</execution>
			</executions>
		</plugin>
		<plugin>
			<groupId>org.eclipse.tycho</groupId>
			<artifactId>tycho-p2-repository-plugin</artifactId>
			<executions>
				<execution>
					<id>archive-repository</id>
					<goals>
						<goal>archive-repository</goal>
					</goals>
					<phase>package</phase>
				</execution>
			</executions>
		</plugin>
	</plugins>
</build>

Now it's sort of a "best of both worlds" deal: I can use the non-deprecated form of the repository and its improved features, while still using the stock NSF Update Site.

This Maven plugin is in OpenNTF's Maven repository, so you can add it in by adding the repo to your root project's pom:

<pluginRepositories>
	<pluginRepository>
		<id>artifactory.openntf.org</id>
		<name>artifactory.openntf.org</name>
		<url>https://artifactory.openntf.org/openntf</url>
	</pluginRepository>
</pluginRepositories>

 

SNTT: Reconstructing DateRanges

Feb 27, 2014 9:53 AM

Tags: sntt

In the OpenNTF API chat, I was reminded yesterday of how terribly broken DateRange retrieval is in Java. Specifically, though you can create date ranges and use them in view lookups, retrieving them from documents or view entries is FUBAR. To wit:

Field Value 02/27/2014, 02/28/2014, 02/27/2014 - 02/28/2014, 01/01/2014 12:00 AM - 01/02/2014 12:00 PM
doc.getItemValue("SomeField") [02/27/2014, 02/28/2014, 02/27/2014, 02/28/2014, 01/01/2014 12:00:00 AM EST, 01/02/2014 12:00:00 PM EST]

Note: this is consistent with LotusScript
doc.getItemValueDateTimeArray("SomeField") [02/27/2014, 02/28/2014, null, null]
session.evaluate(" SomeField ", doc) [02/27/2014, 02/28/2014, 02/27/2014, 02/28/2014, 01/01/2014 12:00:00 AM EST, 01/02/2014 12:00:00 PM EST]
entry.getColumnValues().get(columnIndex) [02/27/2014, 02/28/2014, 02/27/2014, 02/28/2014, 12/29/**** 12:22:46 PM EST, 10/03/**** 04:53:38 PM EDT]

Note: the final values are inconsistent across multiple executions

So it appears that getItemValueDateTimeArray chokes and gives up when it encounters date ranges, instead just plopping a null value in the Vector. View entries appear to wander off into some invalid-data wilderness, perhaps not properly parsing the ITEM_VALUE_TABLE attached to the entry. Or maybe it's in the ITEM_TABLE. I don't remember; it's complicated. In any event, view entries are a mostly-lost cause.

Document item values, though, are salvagable. Since getItemValue returns the start/end values and getItemValueDateTimeArray returns nulls in the spots that represent DateRanges (which are always at the end, but that's incidental), the two can be matched up to reconstruct the real value. I wrote a method to do just this - it doesn't do any checking to make sure that the item value actually contains DateTimes (and would throw a ClassCastException if it contains something else), it should do the job when you know the item contents:

@SuppressWarnings("unchecked")
private List<Base> noSeriouslyGetItemValueDateTimes(final Session session, final Document doc, final String itemName) throws NotesException {
	List<Base> result = new Vector<Base>();

	List<DateTime> dateTimes = doc.getItemValue(itemName);
	List<DateTime> dateTimesArray = doc.getItemValueDateTimeArray(itemName);
	int foreignIndex = 0;
	for(DateTime dt : dateTimesArray) {
		if(dt != null) {
			result.add(dateTimes.get(foreignIndex++));
			dt.recycle();
		} else {
			DateTime dt1 = dateTimes.get(foreignIndex++);
			DateTime dt2 = dateTimes.get(foreignIndex++);
			DateRange range = session.createDateRange(dt1, dt2);
			result.add(range);
		}
	}

	return result;
}

I'll probably add a similar method to the OpenNTF API (or maybe fix getItemValueDateTimeArray), but this should do for now.