Blog

Blog

The conditional (ternary) operator is the only JavaScript operator that takes three operands and it is frequently used as a shortcut for the if statement.

// If isMember (first argument) evaluates to true, return "$2:00" (first expression), 
// else return "$10.00" (second expression)
function getFee(isMember) {
  return (isMember ? "$2.00" : "$10.00");
}


// Another example
// var foo = (a === b) ? 1 : 2;
var foo = (a === b)
  ? 1
  : 2;

See also: Conditional (ternary) Operator, MDN web docs

Lifetime Calendar

This weekend, I got my Lifetime Calendar back online.

View your whole life, in a sobering glance.

https://codyburleson.com/app/lifetime/

A recipe for responding to a button click with an RxJS Observable.

HTML
<script> src="https://unpkg.com/@reactivex/rxjs@5.3.0/dist/global/Rx.js"></script>

<button>Click me</button>
JavaScript
var button = document.querySelector('button');

Rx.Observable.fromEvent(button, 'click').
	subscribe(
		(value) => console.log(value.clientX) // just for example, log the x position of the cursor
	);

JavaScript - Line 3

The observable is created by the operator, fromEvent, which takes an “element” and an event name as parameters. It will listens for events of that name (i.e. 'click') taking place on that element. It returns an Observable that emits those events. An “element” may be a simple DOM element, or a NodeList, jQuery element, Zepto Element, Angular element, Ember.js element, or EventEmitter.

This operator also takes an optional third parameter: a function that accepts the arguments from the event handler as parameters and returns an item to be emitted by the resulting Observable in place of the event.

Alternative importing

If you're using a JavaScript modules approach (such as with TypeScript),  you could do something like the following instead of using the <script> element for importing as shown above.

import { Observable } from 'rxjs/Observable';
import 'rxjs/add/operator/map';
import 'rxjs/add/observable/of';
// import 'rxjs/add/operator/switchMap';
// etc., etc...


// or, you could just more conventiently grab the whole package, but this may not be efficient without Tree Shaking, so I'd recommend the approach above
// import 'rxjs/Rx';

Creating your own observer

Alternatively, you could create your on observer to listen to the click event. Since RxJS provides the fromEvent operator, this is unnecessary. However, showing how this could be done provides a good example of how to create your own observers without use of the RxJS operators.

JavaScript
var button = document.querySelector('button');

var observer = {
	next: function(value) {
		console.log(value.clientX);
	}
	// In reality, the error and complete functions below would never be 
	// called for a button click event, but this shows the basic structure 
	// of an Observable; generally implementing the three functions: next, error, and complete...
	error: function (error) {
		console.log(error);
	}
	complete: function() {
		console.log('Completed');
	}
};


Rx.Observable.fromEvent(button, 'click').
	subscribe(observer);

Creating Observables

There are several kinds of operators that can be used to create new Observables. 

References


Loneliness and a shovel

A twisted little tale for Halloween.


Writing Prompt

Prompt by Cameron Graham, Oct 25 2017, Writing Prompts Group, Facebook

The loneliness was crushing. For six weeks, since the fever had taken her, there had been no one to talk to. The walls would not reply; the looming trees around the cabin had nothing to say; the winds only whipped his words away.

Why did she have to be gone?

He realized she did not have to be gone. Taking up his pick and shovel, he returned to the clearing where he had made her grave.

Writing

He dug madly into the earth. The moon watched over him throughout the night and then finally retired. As the sun rose, he carried her frail and withered body back to rest in her favorite chair beside the hearth where she had sat for all the years of his life. It was from that chair she'd taught him all the most important things to know.

"You can't keep that sweet young girl here, Jimmy," Mama would say. And then she would wait patiently as he pinched his ears and shook his head and moaned and groaned in a fit of anger. She was always so patient. Always so gentle and wise.

Jimmy picked maggots from her flesh and tossed them into the fire. He fetched her crochet needles and placed them gently in her hands. Laid the roll of yarn in her lap and arranged the blanket snug upon her shoulders.

"I know, Mama," he said. "Someone might a come lookin' for that girl."

He waited for her to answer as she had done so many times before. He waited for her to say what she always said, and what he always hated to hear. That's right, Jimmy. And you ought not have her in your bed when they come a knockin'.

"But Mama," he would whine. 

And "Now, Jimmy," she would say, "you know better. You get that girl on outta here now."

He peeked through the door in his bedroom at the child. In the soft glow of the firelight, he could see that she was still there. Still sleeping. So, he sat on the floor by his mama and waited for her to tell him when it was time.

As the day went on, she said many things, but never a sharp word. She never told him to take the girl on out. She had changed. The fever had softened her heart. 

And Jimmy's heart grew full. His loneliness was gone. He had someone to talk to. Like wise old friends, even the trees around the cabin had much to say. The winds sang like a jubilent chorus in their leaves.

If she can stay, why do the others have to be gone too?

He realized they did not have to be gone. Taking up his pick and shovel, he returned to the clearing where he had buried them.



Quantum diamonds

Writing Prompt

Prompt by Evan Austin, Oct 25 2017, Writing Prompts Group, Facebook

While cutting a diamond at the individual atomic level, you discover a language created by quantum physics. Then you discover it opens a portal to another dimension.

Writing

You want to shit a brick? Trust me, you don't. 

Imagine you're up late in the lab and you catch a glimpse of yourself in the bathroom mirror. Not unusual, when there's only one of you in the reflection. But when you see yourself standing behind yourself? Now, that's some shit, man. The brick. That's the brick you shit, that is.

"Listen to me very carefully, I have only time to say this once," I said. 

Not me, or my reflection, said it. It was the me standing behind my reflection - the one that was in the mirror, but not behind me in the room. If you're confused, I understand. Remember, this is my brick your reading about. My sphincter. Figuratively speaking, of course. And I'm here to tell you, or at least I'm pretty sure I'm here to tell you, that ain't nothing. Let me just say it this way for starters. There's not even a word for where this rabbit hole goes. It just goes. And if it ever opens up for you, don't...Jesus, Mary, mother of God, holy moly, holy smokes, do NOT take the bait. 

How would you know? Well, it goes like this.

"The spin calibration is the thirty-two nanos," I said to myself. The weird phantom me, that is. He was dead serious and rushed. "And the medium is wrong! It must be diamonds. Not just any diamonds either. Use Kelly's earrings. She'll forgive you later if she finds out why. If she doesn't, then it doesn't matter 'cause you failed and she'll be dead within the hour."

The weird me flashed in the mirror a few times and disappeared. It was just the two of us left - me and my normal reflection.

"Ha ha," I said aloud. "Pretty fuckin' good guys."

I always thought the nerds at MIT were good at pranks, but at CERN - the shit's at a whole new level, I'm telling you. But to be truthful, I was a little tripped out. Totally sober too. No micro-dose - cross my heart, swear to God - all of them. 

I opened each stall looking for the prankster. When I turned back around my phantom was in the mirror again.

"What is the calibration?"

"Huh?"

"See! I know you," he said. "It's thirty-two. Remember. Thirty-two. Diamonds. Kelly's earrings!"

"Kelly's ears aren't even pierced," I said smugly - proof the pranksters didn't have all their I's dotted and T's crossed.

"Your Kelly is with me now," he said. "My Kelly is at your house. She has the diamonds on. Get them, get back here, run them with the calibration and then give the stats to MEL. This is not a joke. Go now!"

And then, poof...he was gone. 

Just then, a twirp on my smartphone. Selfie from Kelly posing with the earrings. 

“WTF? They’re beautiful,” the text said. “Luv u!!! XOXO nano nano.”


How to install Oracle Java 8 on Ubuntu (in may case, Ubuntu 16.04 LTS)

Procedure to install Oracle Java 8 on Ubuntu

You probably want to confirm first that a version of Java is not already installed. If so, you can do that with the java -version command as shown below...

command as shown below.

developer@ubuntu:~$ java -version
The program 'java' can be found in the following packages:
 * default-jre
 * gcj-5-jre-headless
 * openjdk-8-jre-headless
 * gcj-4.8-jre-headless
 * gcj-4.9-jre-headless
 * openjdk-9-jre-headless
Try: sudo apt install <selected package>
developer@ubuntu:~$ 

Change to root...

sudo su - 
(enter password)
apt-get install software-properties-common
add-apt-repository ppa:webupd8team/java
apt-get update
apt-get install oracle-java8-installer

Check if Java is successfully installed

java -version
java version "1.8.0_111"
Java(TM) SE Runtime Environment (build 1.8.0_111-b14)
Java HotSpot(TM) 64-Bit Server VM (build 25.111-b14, mixed mode)

Procedure to uninstall Oracle Java 8 on Ubuntu

If, for some reason, you want to undo this operation and uninstall Oracle Java 8 from Ubuntu, you can then execute the following:

sudo su -
sudo apt-get remove oracle-java8-installer

Here are the two Docker commands you can run in sequence to completely remove (delete) all of your containers and images.

Docker commands to remove all containers and images
# Remove all containers
docker rm $(docker ps -a -q)
# Remove all images
docker rmi $(docker images -q)

Warning

 Executing the commands shown above will destroy all your containers and images; it will not be possible to restore them.

As a regular or former PC user, you may find yourself confused by how to add environment variables or add items to the PATH on a Mac. Here's how.

You can edit path and other environment variables through either the user profile or the bash profile.

User profile

  • Create a file called .profile in your home directory (i.e. /Users/<username>).
    • Tip: While in a Terminal session, you can always change to your home directory by typing "cd ~" . Type "pwd" to print out and confirm the directory you are currently in.
  • Add environment and path variables in the .profile file as appropriate.
    • After modifying .profile, you'll need to start a new terminal session for the changes to pick up. To make sure everything works properly, you can throw a little echo greeting atop the file. When you start a new terminal, you should see the greeting echoed to you if the file has been recognized by the terminal session.
  • Here is a sample from my own .profile file:


echo "Greetings, Cody!"
export MAVEN_HOME=/Users/codyburleson/Dev/maven
export PATH=${MAVEN_HOME}/bin:${PATH}
export MAVEN_OPTS=-Xms256m -Xmx512m                                         


Bash profile

You can also do the same by editing the bash profile...

If you want to add or edit other environment variables, you can modify the bash profile...

sudo nano ~/.bash_profile

For example, you could add lines similar to the following to setup HOME variables for important applications...

export JAVA_HOME=/Library/Java/JavaVirtualMachines/jdk1.8.0_73.jdk/Contents/Home
export ANT_HOME=/Users/cburleson/apache-ant-1.10.1


Adding paths only

You can modify the path by simply adding paths in the /etc/paths file. You might find this simpler than building up an export PATH line in one of the profile methods described above.

Use the following command to open the file in the nano editor...

sudo nano /etc/paths

For example, here's a look at the contents of my paths file after adding Apache Ant...

/usr/local/bin
/usr/bin
/bin
/usr/sbin
/sbin
/Users/cburleson/apache-ant-1.10.1/bin


After you've added the path and saved the change, you will need to open a new terminal to validate that the changes have taken effect.



Vanessa Foster's account of a misguided youth, fleeing the FBI from Texas all the way to Alaska, is everything a great memoir should be. It's a love-crazed and drug-twisted adventure where one mishap after another unravels her world until little remains, but a helpless wish for grace and a humbled reliance on the aid of culinary angels. It's also a poetic search through the colorful, but frayed and fading pieces of a once hopeful reality. She gropes desperately for a thread of meaning while fumbling with her exhilarating lover in his schizophrenic downward spiral. With all hopes, dreams, family, and innocence seemingly lost, she is finally relented with a gift of grace, illuminated with spiritual truth, and crowned a woman. In losing almost everything, she gains something that is more.

Vanessa Foster delivers all the things we seek from a good story - escape, adventure, entertainment, spiritual retreat and self-discovery. And it's all true.

As a wanna-be writer, I have to envy her. The book reminded me of The Road by Cormac McCarthy. It's not a similar story, but she's similar in her ability to break all the rules of writing in eloquent service of honesty and poetic truth. Like McCarthy, she dismisses quotation marks from dialog in a way that makes the words of others somehow also her own - a technically challenging feat that paints every external scene with vivid emotions from her own mind - her own experience of each moment. In this, she enables us to feel what she felt and to see things as she saw them. We become her. We become who she was, and then we become with her as she changes into something new. Ultimately, we are left with the memory of an experience that might as well have been our own - complete with its hard-earned rewards, and yet all under the safety of our bedside reading lamp.

Today, finding a good book, much less a great one, is a difficult task; they are few and far between. But if you're anything like me, I think you'll find that Vanessa Foster's More Than Everything is at least more than many.

Here's how to use query parameters from the HTPP request (or from the URL) in a personalization component. This page came from my raw notes and hasn't yet been rewritten for the broader audience; sorry. Posting it anyway for those who won't mind a little sloppiness.



The personalization component must be rendered by the WCM servlet (the URL of which you can get from Previewing the component - DONT FORGET TO REMOVE NO CACHE PARAMS IN PROD). If you access it as rendered within the Web Content Viewer, the rule will not have access to the Request and won't be able to get the request parameters.


Assuming the following taxonomy and categories...


And assuming the following content...


We're going to create the following PZN rule...

We created 3 request parameters so that we can enforce a rule that insists that all 3 must be on the content (AND). With this, we can add the following to the URL:

&cat1=testcat1&cat2=testcat2&cat3=testcat3

We could alternatively have only 1 request param and do something like this:

&cats=testcat1,testcat2,testcat3

The problem with that is that it will only enforce an OR evaluation. If any content is tagged with any one of the given categories, it will show up.

Because we need to ensure that only content tagged with all 3 categories shows up, we create 3 separate request params. This allows us to use the PZN rule to enforce the AND condition.


Here's how you make establish the request parameters and make them available in the pzn rule.


Step one: Click the value slot...


Step two: Navigate to Manage Properties... under the Request item...

When you're just starting, you list may be empty (mine has some examples in it already). 


Step Three: Click Add Dynamic Property...

Step four: Fill out as follows...for example...


Click SAVE.

Click DONE.

Your new item should show in the list under Request...






Preview the PZN Component. Remove the unecessary params (like no chache) from the URL for PROD. Add your parameters and category names to the URL. For example:


http://local.base22.com:10039/wps/wcm/myconnect/web%20content/articles?source=library&srv=cmpnt&cmpntid=0d99a109-b8c7-4f38-a9ae-8d48faf37e32&cat1=testcat1&cat2=testcat2&cat3=testcat3


And you get:




 

The IBM Digital Experience suite includes IBM WebSphere Portal, IBM Web Content Manager, IBM Forms Experience Builder, and IBM Web Experience Factory. Following is my curated list of various reference resources related to these products.

Click on headers to sort columns in the table below.

ResourceAbstractTopicSub-topicResource TypeSourcePublished or Last Mod
Adding Bootstrap to a Portal ThemeThis article explains the steps needed to add Bootstrap to a modularized Portal theme:Theme DevelopmentBootstrap
IBM developerWorks, IBM Digital Experience Developer Center2016-01-29
Bootstrap meets PortalHow to integrate Twitter Bootstrap with WebSphere Portal and provide the different artifacts that are required. Theme DevelopmentBootstrap


Simple Theme Custom EAR File Creation

This sample allows you to quickly make a copy of the Simple Theme ear files and install your own custom version. This will allow you to change the jsps and dynamic content spots in the theme.


Theme DevelopmentSimple Theme
GitHub, Digital Experience2016-01-26
Simple Theme Bootstrap LayoutsThis sample contains four sample layouts which use the Bootstrap framework grid system, which is included in the Simple Theme's static files. Theme DevelopmentBootstrap

2016-01-26
Theme PAA for IBM WebSphere PortalProvides access to the Portal 8.5 Theme as a PAA deliverableTheme Development



Developing themes and skins
Theme Development
Product Documentation

Getting started with Themes
Theme Development
Product Documentation

Theme Manager


Theme Development
Product Documentation

DX Performance Testing Substitution Patterns for Reusable Test SuitesThis article will give you an overview over common portal specific substitution parts when writing reusable & dynamic performance tests. This article will not give you any concrete examples based on a specific tool like JMeter or Rational Performance Tester. Instead all examples consist of a little snippet and a potential regular expression to find the areas for substitution.Performance Testing and Tuning



Portal V8.5 Performance Tuning Guide
Performance Testing and Tuning



Resolver Samples for WebSphere PortalIllustration of late binding to an application using the resolver framework in WebSphere Portal



2015-09-20
How to create a custom HTML editor integration You can use custom HTML editors in all HTML fields of the authoring interface or specific HTML elements that are defined in an authoring template. Custom HTML fields are used to integrate third-party editors into the authoring interface.




Sample Custom Script Plugins for WCMSamples of WCM Plugins with a JavaScript implementation




IBM Digital Experience Developer CenterFor developers who use IBM Digital Experience products and tools




IBM Digital Experience V8.5 API and Developer Reference Documentation

API, SPI, and other reference documentation for developers is provided here for convenience. The most current documentation is installed with the respective products.

SDK and API
Site

Sample Node.js Build Tools for Script PortletSample Node.js Build Tools for Script Portlet



2015-07-08
WCM Sample Custom Workflow ActionsSample custom workflow actions for IBM Web Content Manager



2014-07-19
WCM Syndication HealthcheckPeriodically update a content item on the authoring server and verify that the update appears on the rendering server.



2014-06-06
RSS Sample for IBM Digital Data Connector in WebSphere PortalRSS Sample for IBM Digital Data Connector in WebSphere Portal - integrate remote RSS data without writing code and style it with Web Content Manager



2014-07-04
IBM Digital Experience Wiki





IBM Digital Experience YouTube Channel





IBM ADL4WCM YouTube Channel





WCM 8.5 - CF07 - Storing Translated Strings as Content

Previously, the strings used by the text provider plug-in had to be stored in a custom plug-in. Now they can be stored in content items and site areas, allowing users to create and maintain text provider strings from within Web Content Manager.
Multilingual

2015-07-28
IBM Forms Experience Builder 8.6.4This documentation set contains information for both administrators who install and configure the product, and users who design and build IBM® Forms Experience Builder applications.

Product Documentation

What's new with CF08 - IBM

What's new with Combined Cumulative Fix 08.

IBM®
IBM Knowledge Center



What's new with CF07 - IBM

What's new with Combined Cumulative Fix 07.




What's new with CF06 - IBM







What's new with CF03 - IBM







Web Developer Toolkit for IBM Digital Experience





Leveraging the Power of Web Content Manager Within a Portal Theme


Theme Development



A Step-By-Step Guide to performing a standalone WebSphere Portal 9.0 installationThis guide is meant to provide a step-by-step for the setup and installation of a standalone IBM WebSphere Portal 9.0. We will also cover prerequisites



2017-01




IntelliJ IDEA cheat sheet

About

Keyboard shortcuts and various tips for IntelliJ IDEA. This document assumes IntelliJ on a Mac, using the Keymap setting for Mac OS X 10.5+ (in Preferences > Keymap). I know you can get all these through Help > Keymap Reference in IntelliJ, but I like to maintain my own list so that I can organize and search it the way I prefer, plus have a place to add my own additional notes.

Editing

Comment / uncomment selected textSelect block of text and then press Command + /
Spread selection from cursor (to word, then line, etc.)ALT + up arrow or down arrow
Format selected codeCTRL + ALT + I
Format all codeALT + CMD + L
Autocomplete method parameter infoCMD + P (when cursor is in method signature)
Invoke a Surround Template (ex: surround a line with try/catch)OPTION + CMD + T
Complete a statementSHIFT + CMD + ENTER
Import Java PackageHover over the red text of the class missing a package and press:
ALT + ENTER

Live Templates

Live templates can be found in Preferences > Editor > Live Templates. Learn them!

To use a live template, enter its short text in the editor and then press tab to replace the short text with its template.

Navigation

Tool Windows and Dialogs

ProjectCMD + 1
Version ControlCMD + 9
PreferencesCMD + ,
BreakpointsSHIFT + COMMAND + F8
Recent FilesCMD + E
Search EverywhereSHIFT + SHIFT
Search ClassesCMD + O
Search SymbolsOPTION + CMD + O
Navigate back and forth through recent filesCMD + [ and CMD + ]
New file (where selection is in Project window)

CMD + N

Note: you can use slashes to create entire trees abc/def/ghi/jkl...

Hide all other windowsSHIFT + CMD + F12
Stretch pane right or leftSHIFT + CMD + left or right arrow
Clipboard HistorySHIFT + CMD + V
Method List (of the active editor file)CMD + F12
Navigation Bar

CMD + up arrow

Note: This is particularly useful if you turn off the normal navigation bar in preferences to save UI space.

Action SearchSHIFT + CMD + A (try, for example, "RESTful Web Service")
Version Control Quick ListCTRL + V
Quick Switch SettingsCTRL + ~
Quick DocumentationCTRL + J (and then again to pin it open at larger size).


TO DO items

Create a TO DO item

  • CMD + / to create a new comment line
  • Type TODO then whatever comment you want

View all TO DO items

  • View > Tool Windows > TODO or
  • CMD + 6

Increase IntelliJ IDE memory on a Mac

  •  Help > Edit Custom VM Options...
  • Answer Yes to create the idea.vmoptions file. This will automatically create a copy of the .vmoptions file in the config folder and open a dialog to edit it.
  • I change -Xmx750m to -Xmx2048m

References

Recently I completed a little study incorporating Spring for Stardog into a Spring Boot web app, with successful results. Here's how.

Contents

Introduction

Stardog is a Java based RDF repository server (a.k.a. triple-store and more), which supports the RDF graph data model; SPARQL query language; property graph model and Gremlin graph traversal language; HTTP and SNARL protocols for remote access and control; OWL 2 and user-defined rules for inference and data analytics; virtual graphs; geospatial query answering; and programmatic interaction via several languages and network interfaces. At this point, I don't have a lot of experience with Stardog, but I've been experimenting with it to see what I can learn. As part of my study, I recently incorporated Stardog Spring into a Spring Boot web app - taking notes along the way. Here's my notes on how I got it setup and working successfully.

Download and Install Stardog

For my local development environment on Mac OS, I installed Stardog 5 BETA (Community Edition).

You can download Stardog at www.stardog.com. Once you've downloaded it, unzip the archive to a destination directory. I'm put mine in /Users/cburleson/stardog

Next you need to set the STARDOG_HOME environment variable. You can do this by adding an export line to your .bash_profile.

cd ~
sudo nano .bash_profile

Add the following line:

Add to .bash_profile
export STARDOG_HOME=/Users/cburleson/stardog

Save changes made to .bash_profile by hitting Control+o (that’s an o as in otter), ENTER, then exit out of nano by hitting Control+X.

Note that changes made .bash_profile will require the shell to be restarted or a new shell to spawn.

Copy stardog-license-key.bin into the STARDOG_HOME location. Then you can start the Stardog server to test your installation.

Start the Stardog server
/Users/cburleson/stardog/bin/stardog-admin server start

If everything is working properly, you should get the following response.

Stardog server startup response
ERROR 2017-05-09 14:34:56,823 [main] com.complexible.stardog.protocols.http.server.StardogHttpServiceLoader:createService(161): Unable to create an instance of HttpService class com.complexible.stardog.protocols.http.server.virtual.admin.VirtualGraphHttpService, missing compatible constructor

Stardog server 5.0-beta started on Tue May 09 14:34:57 CDT 2017.

Stardog server is listening on all network interfaces.
HTTP server available at http://localhost:5820.

STARDOG_HOME=/Users/cburleson/stardog

Note that there is an ERROR in this response, but I think it just has to do with the fact that I'm using the Community edition and not an enterprise edition (see: this support issue)

Still, the server is running and can be accessed in a web browser at  http://localhost:5820/


Login with:


Usernameadmin
Passwordadmin


You can also check the server status with the following command:

Check Stardog server status
/Users/cburleson/stardog/bin/stardog-admin server status

Now, that you've tested the installation, you can stop the server with the following command.

/Users/cburleson/stardog/bin/stardog-admin server stop


Add the Stardog Maven Repo to pom.xml

In order to get the required dependencies, you need to add the Stardog public maven repository to your repositories defined in the Maven POM (pom.xml). Here's how that section looks in my file:

Stardog Maven repo in pom.xml
<repositories>

    <repository>
        <id>spring-releases</id>
        <url>https://repo.spring.io/libs-release</url>
    </repository>

    <repository>
        <id>stardog-public</id>
        <url>http://maven.stardog.com</url>
    </repository>

</repositories>

Add Stardog Dependencies to pom.xml

Now, we can add the required dependencies. Notice that even though I installed Stardog 5 BETA, I'm using a different version number for various dependencies (still, it works).

Stardog dependencies in pom.xml
<dependency>
    <groupId>com.complexible.stardog</groupId>
    <artifactId>client-snarl</artifactId>
    <version>4.2.4</version>
    <type>pom</type>
</dependency>


<dependency>
    <groupId>com.complexible.stardog</groupId>
    <artifactId>client-http</artifactId>
    <version>4.2.4</version>
    <type>pom</type>
</dependency>


<dependency>
    <groupId>com.complexible.stardog</groupId>
    <artifactId>server</artifactId>
    <version>4.2.4</version>
    <type>pom</type>
</dependency>

<!--
<dependency>
    <groupId>com.complexible.stardog.sesame</groupId>
    <artifactId>stardog-sesame-core</artifactId>
    <version>4.2.4</version>
    <type>jar</type>
</dependency>
-->
<!--
<dependency>
  <groupId>com.complexible.stardog.jena</groupId>
  <artifactId>stardog-jena</artifactId>
  <version>4.2.4</version>
  <type>jar</type>
</dependency>
-->

<!--
<dependency>
    <groupId>com.complexible.stardog.gremlin</groupId>
    <artifactId>stardog-gremlin</artifactId>
    <version>4.2.4</version>
    <type>jar</type>
</dependency>
-->


<dependency>
    <groupId>com.complexible.stardog</groupId>
    <artifactId>stardog-spring</artifactId>
    <version>2.1.3</version>
    <type>jar</type>
</dependency>


<!--
<dependency>
    <groupId>com.complexible.stardog</groupId>
    <artifactId>stardog-spring-batch</artifactId>
    <version>2.1.3</version>
    <type>jar</type>
</dependency>
-->

<!-- https://mvnrepository.com/artifact/org.apache.lucene/lucene-core -->
<dependency>
    <groupId>org.apache.lucene</groupId>
    <artifactId>lucene-core</artifactId>
    <version>6.5.1</version>
</dependency>

Notice that I've got stardog-spring-batch commented in the file, but commented out. I put it in the file incase I decide to use it in the future, but for now, I don't need it.

Create or Edit Spring Application Context File

I prefer using pure Java only configuration for Spring, but I had trouble with this one, so opted to use the XML configuration. I created the following file inside of src/main/resources...

applicationContext.xml
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">

    <bean name="embeddedProvider" class="com.base22.rdfx.config.EmbeddedProvider"></bean>

    <bean name="dataSource" class="com.complexible.stardog.ext.spring.DataSourceFactoryBean">
        <property name="to" value="testdb"/>
        <property name="provider" ref="embeddedProvider"/>
        <property name="username" value="admin"/>
        <property name="password" value="admin"/>
    </bean>

    <bean name="template" class="com.complexible.stardog.ext.spring.SnarlTemplate">
        <property name="dataSource" ref="dataSource"/>
    </bean>

    <bean name="importer" class="com.complexible.stardog.ext.spring.DataImporter">
        <property name="snarlTemplate" ref="template"/>
        <property name="format" value="N3"/>
        <property name="inputFiles">
            <list>
                <value>classpath:data/sp2b_10k.n3</value>
            </list>
        </property>
    </bean>

</beans>

Notice that the data importer bean is going to look for an RDF file in the classpath, which should exist in src/main/resources/data.

I got that file from the source project for stardog-spring, which you can find on GitHub at:  https://github.com/stardog-union/stardog-spring

You could use any RDF file that you want to have auto-loaded into the Stardog repository.

Make the applicationContext.xml Available to Your Spring Boot App

In order for your Spring Boot app to recognize and load the applicationContext.xml, you'll need to add an annotation to the main application class (the one with a Java main() method)...

The annotation you'll need to add is:

@ImportResource("classpath:applicationContext.xml")

My main application class looks like this:

Platform.java
package com.base22.rdfx;
 
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.annotation.ImportResource;
 
@SpringBootApplication
@ImportResource("classpath:applicationContext.xml")
public class Platform {
 
    public static void main(String[] args) throws Exception {
        SpringApplication.run(Platform.class, args);
    }
 
}


Create an EmbeddedProvider Java Class

The applicationContext.xml file references an EmbeddedProvider class. There is no EmbeddedProvider exposed by the stardog-spring library, but I found one in the stardog-spring source code at src/main/test/java/com/complexible/stardog/ext/spring.

You'll need to create this class in your project and make sure that you reference the package and class properly for the embeddedProvider bean in your applicationContext.xml file.

EmbeddedProvider.java
package com.base22.rdfx.config;

import com.complexible.common.protocols.server.ServerException;
import com.complexible.stardog.Stardog;
import com.complexible.stardog.StardogException;
import com.complexible.stardog.api.admin.AdminConnection;
import com.complexible.stardog.api.admin.AdminConnectionConfiguration;
import com.complexible.stardog.ext.spring.Provider;
import com.complexible.stardog.protocols.snarl.SNARLProtocolConstants;

public class EmbeddedProvider implements Provider {

   @Override
   public void execute(String to, String url, String user, String pass) {

      try {
         Stardog.builder()
               .create()
               .newServer()
               .bind(SNARLProtocolConstants.EMBEDDED_ADDRESS)
               .start();
         AdminConnection dbms = AdminConnectionConfiguration.toEmbeddedServer().credentials(user, pass).connect();
         if (dbms.list().contains(to)) {
            dbms.drop(to);
            dbms.createMemory(to);
         } else {
            dbms.createMemory(to);
         }
         dbms.close();
      } catch (StardogException e) {

      } catch (ServerException e) {

      } finally {

      }

   }
}


Use the Spring for Stardog SnarlTemplate in a Controller

For a quick acid test, I created a simple Spring Controller that gets executed when you hit the path /test in a web browser.

Here's the simple test controller I created, which logs output from a SPARQL query to the console.

TestController.java
package com.base22.rdfx.controllers;

import com.complexible.stardog.ext.spring.SnarlTemplate;
import com.complexible.stardog.ext.spring.mapper.SimpleRowMapper;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;

import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;

@RestController
public class TestController {

   private static final Logger log = LoggerFactory.getLogger( TestController.class);

   @Autowired
   private SnarlTemplate template;

   //private static final String template = "Hello, %s!";
   //private final AtomicLong counter = new AtomicLong();

   @RequestMapping("/test")
   public void test(@RequestParam(value="name", defaultValue="World") String name) {

      log.trace(">> test()");

      String sparql = "SELECT ?s ?p ?o WHERE { ?s ?p ?o } LIMIT 5";

      List<Map<String,String>> results = template.query(sparql, new SimpleRowMapper());

      for ( Map<String, String> result : results ) {

         log.trace( "-- test() > --------------------------");

         Set keys = result.keySet();

         for ( Iterator i = keys.iterator(); i.hasNext(); ) {
            String key = (String) i.next();
            String value = result.get(key);
            log.trace("-- test() > " + key + " | " + value);
         }

      }

   }

}

How to Use the SnarlTemplate

The Spring Programming section of the documentation on the Stardog website provides some good information. However, if you want to see some actual code examples, you might want to refer to the TestDataSourceFactory.java class on GitHub. That's where I learned how to execute the query shown in my TestController using a SimpleRowMapper.

Test the App

Now you should be able to run your Spring Boot application and hit the TestController (/test in your browser). When you hit the URL, you should see the following output logged to the console, which shows that you've successfully configured and used Spring for Stardog. As you can see, five triples were returned from the given LIMIT 5 SPARQL query...

14:40:36.903 [http-nio-8080-exec-1] TRACE com.base22.rdfx.controllers.TestController - -- test() > --------------------------
14:40:36.903 [http-nio-8080-exec-1] TRACE com.base22.rdfx.controllers.TestController - -- test() > p | http://www.w3.org/2000/01/rdf-schema#subClassOf
14:40:36.903 [http-nio-8080-exec-1] TRACE com.base22.rdfx.controllers.TestController - -- test() > s | http://localhost/vocabulary/bench/Journal
14:40:36.903 [http-nio-8080-exec-1] TRACE com.base22.rdfx.controllers.TestController - -- test() > o | http://xmlns.com/foaf/0.1/Document
14:40:36.903 [http-nio-8080-exec-1] TRACE com.base22.rdfx.controllers.TestController - -- test() > --------------------------
14:40:36.903 [http-nio-8080-exec-1] TRACE com.base22.rdfx.controllers.TestController - -- test() > p | http://www.w3.org/2000/01/rdf-schema#subClassOf
14:40:36.903 [http-nio-8080-exec-1] TRACE com.base22.rdfx.controllers.TestController - -- test() > s | http://localhost/vocabulary/bench/Proceedings
14:40:36.903 [http-nio-8080-exec-1] TRACE com.base22.rdfx.controllers.TestController - -- test() > o | http://xmlns.com/foaf/0.1/Document
14:40:36.903 [http-nio-8080-exec-1] TRACE com.base22.rdfx.controllers.TestController - -- test() > --------------------------
14:40:36.903 [http-nio-8080-exec-1] TRACE com.base22.rdfx.controllers.TestController - -- test() > p | http://www.w3.org/2000/01/rdf-schema#subClassOf
14:40:36.903 [http-nio-8080-exec-1] TRACE com.base22.rdfx.controllers.TestController - -- test() > s | http://localhost/vocabulary/bench/Inproceedings
14:40:36.903 [http-nio-8080-exec-1] TRACE com.base22.rdfx.controllers.TestController - -- test() > o | http://xmlns.com/foaf/0.1/Document
14:40:36.903 [http-nio-8080-exec-1] TRACE com.base22.rdfx.controllers.TestController - -- test() > --------------------------
14:40:36.903 [http-nio-8080-exec-1] TRACE com.base22.rdfx.controllers.TestController - -- test() > p | http://www.w3.org/2000/01/rdf-schema#subClassOf
14:40:36.903 [http-nio-8080-exec-1] TRACE com.base22.rdfx.controllers.TestController - -- test() > s | http://localhost/vocabulary/bench/Article
14:40:36.903 [http-nio-8080-exec-1] TRACE com.base22.rdfx.controllers.TestController - -- test() > o | http://xmlns.com/foaf/0.1/Document
14:40:36.903 [http-nio-8080-exec-1] TRACE com.base22.rdfx.controllers.TestController - -- test() > --------------------------
14:40:36.903 [http-nio-8080-exec-1] TRACE com.base22.rdfx.controllers.TestController - -- test() > p | http://www.w3.org/2000/01/rdf-schema#subClassOf
14:40:36.903 [http-nio-8080-exec-1] TRACE com.base22.rdfx.controllers.TestController - -- test() > s | http://localhost/vocabulary/bench/Www
14:40:36.903 [http-nio-8080-exec-1] TRACE com.base22.rdfx.controllers.TestController - -- test() > o | http://xmlns.com/foaf/0.1/Document

Conclusion

In this post, I showed how I used Spring for Stardog in a Spring Boot web app. With some minor variation, these instructions could probably be useful for any Spring app and not just a Spring Boot web app. 

There is also some useful information in the QUICKSTART.txt file on GitHub that you might find useful, so be sure to check it out.



Some very rough notes I took while learning to create an Atlassian Confluence plugin. Perhaps, I'm in the process of cleaning these notes up now, while building another plugin. Please consider this a somewhat sloppy WORK-IN-PROGRESS for now.


Contents

Install the Atlassian SDK

See: Set up the Atlassian Plugin SDK and Build a Project

Create the Plugin Skeleton

Navigate to the directory on your system where you'd like to create your plugin.  The command we are about to run will create a folder with the plugin directories inside.

mkdir confluence-code-mojo

Change into the directory...

cd confluence-code-mojo

Execute the following command to generate the plugin skeleton...

atlas-create-confluence-plugin

The first time, Maven will download a bunch of packages.

Now you need to define some things:

Define value for groupId: : com.codyburleson.confluence
Define value for artifactId: : codemojo
Define value for version: 1.0.0-SNAPSHOT: (press ENTER)
Define value for package: com.codyburleson.test: : com.codyburleson.confluence.codemojo

You will then be prompted to confirm

Confirm properties configuration:
groupId: com.codyburleson.confluence
artifactId: codemojo
version: 1.0.0-SNAPSHOT
package: com.codyburleson.confluence.codemojo
 Y: : (press ENTER)

Maven will do its thing. If everything goes well, you should then get a BUILD SUCCESS message...

[INFO] ------------------------------------------------------------------------
[INFO] BUILD SUCCESS
[INFO] ------------------------------------------------------------------------
[INFO] Total time: 02:23 min
[INFO] Finished at: 2017-06-23T14:55:21-06:00
[INFO] Final Memory: 13M/160M
[INFO] ------------------------------------------------------------------------

Now, we should have the basic plugin skeleton within the initial directory as follows:

  • confluence-code-mojo/
    • codemojo/
      • LICENSE
      • pom.xml
      • README
      • src/
        • etc...

I'm using IntelliJ IDEA as my IDE, so, at this point, I can choose to import the project in IntelliJ (selecting the pom.xml file within the codemojo/ directory.)

It's a good idea to establish this starting point as a baseline from which to build a source code history. I'll track with Git, so cd into the confluence-code-mojo folder and execute the following command to initialize the containing directory as a Git repository.

git init

Now, you can open the local repository in Atlassian SourceTree (Git client) or use the command line; whatever you prefer to manage and track your source code changes. Personally, I prefer to use SourceTree. In SourceTree, I select  New... > Add Existing Local Repository and then choose the confluence-code-mojo folder. I select all files in the File status view, enter the Commit message, "Initial commit" and then press Commit and OK.

Now we're ready to start customizing the plugin, but before we do that, let's just do a little acid test.

Install and test the plugin skeleton

Change into the plugin directory (in my case, codemojo) and enter the following command: 

atlas-run

The Atlas CLI and Maven will do their thing - bringing down required dependencies and then firing up developer instance of Confluence with your plugin installed for testing purposes.

Wait patiently, this process takes quite a while to complete.

When it's ready, you should see something like the following in the terminal:

[INFO] [talledLocalContainer] Jun 23, 2017 3:21:35 PM com.sun.jersey.server.impl.application.WebApplicationImpl _initiate
[INFO] [talledLocalContainer] INFO: Initiating Jersey application, version 'Jersey: 1.8-atlassian-16 03/23/2015 10:20 PM'


Navigate to:

http://localhost:1990/confluence

Login with admin | admin

Go find your plugin in the manage page...

http://localhost:1990/confluence/plugins/servlet/upm/manage/all


Note

After executing the atlas-run command, use CTRL + D in the console to shut down gracefully! But, let's not shut it down just yet. We need to make changes to the plugin and redeploy.

Customize the plugin


Open the src/main/resources/atlassian-plugin.xml file in your editor. It should look something like this:

atlassian-plugin.xml
<atlassian-plugin key="${atlassian.plugin.key}" name="${project.name}" plugins-version="2">
    <plugin-info>
        <description>${project.description}</description>
        <version>${project.version}</version>
        <vendor name="${project.organization.name}" url="${project.organization.url}" />
        <param name="plugin-icon">images/pluginIcon.png</param>
        <param name="plugin-logo">images/pluginLogo.png</param>
    </plugin-info>

    <!-- add our i18n resource -->
    <resource type="i18n" name="i18n" location="codemojo"/>
    
    <!-- add our web resources -->
    <web-resource key="codemojo-resources" name="codemojo Web Resources">
        <dependency>com.atlassian.auiplugin:ajs</dependency>
        
        <resource type="download" name="codemojo.css" location="/css/codemojo.css"/>
        <resource type="download" name="codemojo.js" location="/js/codemojo.js"/>
        <resource type="download" name="images/" location="/images"/>

        <context>codemojo</context>
    </web-resource>
    
</atlassian-plugin>


After the <web-resource>...</web-resource> section in the file, enter the following to setup macro plugin type.


<xhtml-macro name="codemojo" class="com.codyburleson.confluence.codemojo.macro.Plugin" key='codemojo'>
    <description key="my.plugin.desc"/>
    <category name="formatting"/>
    <parameters/>
</xhtml-macro>

Open the file /src/main/resources/codemojo.properties and add edit similar to the following:

#put any key/value pairs here
my.plugin.name=CodeMojo Macro
my.plugin.desc=A source code formatting macro that uses Codemirror for advanced formatting.

Also, change the <description> item in the pom.xml; this is what will display as a description in the Add-ons management area of Confluence. For example:


<name>codemojo</name>
<description>Macro and formatters for pretty-printing source code in a wiki page using Codemirror.</description>
<packaging>atlassian-plugin</packaging>


Create the com.codyburleson.confluence.codemojo.macro java package.

Next, create the Java class file, Plugin.java, in that package...

Panel.java
package com.codyburleson.confluence.macro;

import com.atlassian.confluence.content.render.xhtml.ConversionContext;
import com.atlassian.confluence.macro.Macro;
import com.atlassian.confluence.macro.MacroExecutionException;

import java.util.Map;

public class Panel implements Macro {

    public String execute(Map<String, String> map, String s, ConversionContext conversionContext) throws MacroExecutionException {
        return "<h1>Hello World</h1>";
    }

    public BodyType getBodyType() { return BodyType.NONE; }

    public OutputType getOutputType() { return OutputType.BLOCK; }
}

This is the minimum skeleton your Macro will require to implement the confluence Macro class and display a Macro object in Confluence. 

In your terminal window, change directory back to the top directory for your plugin (eg cd /Users/cburleson/repos/confluence-code-mojo/codemojo)

Run the command:

atlas-mvn package

You should see a confirmation message 

[INFO] BUILD SUCCESS
[INFO] ------------------------------------------------------------------------
[INFO] Total time: 4.656 s
[INFO] Finished at: 2016-10-10T18:33:09+10:00
[INFO] Final Memory: 37M/433M
[INFO] ------------------------------------------------------------------------

I, on the other hand, got an error message on the attempt to redeploy in the atlas-run session:

[INFO] [talledLocalContainer] Caused by: com.atlassian.plugin.IllegalPluginStateException: Cannot getResourceAsStream(codemojo_en_GB.properties): This operation must occur before the plugin 'com.codyburleson.confluence.codemojo' is uninstalled

I created a codemojo_en_GB.properties file right next to the codemojo.properties file. But since the file wasn't there initially, I need to stop the atlas-run session and restart it. From then on, redeploying with the atlas-mvn package command should work. 

That is, press CTRL + D to shut down the former atlas-run session. 

Run atlas-mvn package to build the plugin.


Typically, when you run the atlas-mvn package command, you can monitor the terminal where confluence was run originally using atlas-run and confirm that QuickReload finished loading. You should see a confirmation message. That's what we'll do to keep deploying changes to our plugin (once passing the above little quirk).


TO DO

I should consult with Atlassian about this little quirk.

Now you can try adding the Macro to a test page in Confluence (you'll need to make a new Confluence Space and Page before you can test it out so go ahead and do that first).


Now, we will continue to customize the plugin. We'll add a macro parameter to learn about how parameters can be set, and used.

Note to Self (and potential readers)

In my second plugin development, through which I am editing this page to try to make it more accurate and useful, I left off here. Going forward from this point, it could get confusing; sorry. I'll be back to finish up soon!


Open the atlassian-plugin.xml file in your editor.

Locate the <parameters/> element within the <xhtml-macro> element you created.  

Replace the <parameters/> element with the following:

<parameters>
    <parameter name="Title" type="string" />
</parameters>

This specifies that the parameter is called 'Title' and is of type 'string'.  You can find the full list of types under the Parameters heading in the macro module documentation

Now modify the execute method in the Java class so it looks like this:

public String execute(Map<String, String> map, String s, ConversionContext conversionContext) throws MacroExecutionException {
    if (map.get("Title") != null) {


		StringBuilder sb = new StringBuilder();
   		sb.append("<div class=\"codemojo\">");
   		sb.append(map.get("Title"));
   		sb.append("</div>");
   		return sb.toString();
	} else {
   		return "<h1>Hello World!<h1>";
	}

}

Execute atlas-mvn package.


Linux and Unix commands

A cheatsheet for common Linux / Unix commands.


Contents

Change File Permissions

CommandUsage
chmod +x filename.extGive execute access to a file.
sudo chmod -R 777 workspace-R means RECURSIVE
sudo chown -R basejump workspaceTake ownership of a file.
basejump (user) and workspace (directory) 

Compress or Extract Files


CommandUsage
tar -zxvf file.tar.gzTo extract one or more members from an archive:
tar -czvf name-of-archive.tar.gz /path/to/directory-or-file

Compress an entire directory or single file.

Note: Be sure to add the .tar.gz in the archive file name yourself; the command doesn't do it for you. 


Here’s what the switches mean:

-c: Create an archive.
-z: Compress the archive with gzip.
-v: Display progress in the terminal while creating the archive, also known as “verbose” mode. The v is always optional in these commands, but it’s helpful.
-f: Allows you to specify the filename of the archive.


Create an Alias for a Common Command

CommandUsage
alias p=<command>Create shortcut aliases to common commands.
For example:
alias p="open /x/yx/z" to open a particular directory in Finder

Directory Commands


CommandUsage
mv <oldDirName> <newDirName>Rename a directory. Be aware: this is actually a move command. So, you're just moving the directory from one name to another in the same path.
rmdirRemove an empty directory
rm -rf <dirName>

Forcefully remove a directory recursively.

Remove the f switch to be prompted for each sub-directory and file inside.

cp -rf present/directory /desire/directory

Copy an entire directory, its subdirectories, and files.

Remove the f switch to be prompted for each sub-directory and file inside.

Execute Command as Root User

CommandUsage
sudo <command>Execute command as the root user.
sudo !!Execute the last command as root user.
(!!) represents the last command you just tried to run, but couldn't because of permission issues.

Execute Task in Background

CommandUsage
./startTest.sh &Start the execution in the background, which will allow you to kill your SSH terminal without killing the process itself.

Find Large Files

CommandUsage
cd /
find . -size +10000000c -print
Prints out the names of all files with size > 10mb.
cd /
sudo du -sm * 
Examine the size of the directories under /.
You can then navigate into any given subdirectory and execute dm -sm * again to see which subdirectories are the largest. 

Inspect Disk Space and Usage

CommandUsage
df -hInspect disk space and usage (in MB or GB)


Work with Environment Variables

CommandUsage
printenvPrints all currently set environment variables and values.

Remove a File


CommandUsage
mvRename a file 
rmRemove a file 
rm -fForcefully remove a file
rm -iInteractively remove a file

If you are not certain about removing files that match a pattern you supply, it is always good to run rm interactively (rm –i) to prompt before every removal.


Switch User

CommandUsage
su - <username>Switch to the given user, loading their profile.
You may have to use sudo su - <username>. 
suWithout a username means to just switch to root user.
whoami"Who Am I?" Prints the current user. 


View a File

CommandUsage
catUsed for viewing files that are not very long; it does not provide any scroll-back.
tacUsed to look at a file backwards, starting with the last line.
lessUsed to view larger files because it is a paging program; it pauses at each screenful of text, provides scroll-back capabilities, and lets you search and navigate within the file. Note: Use / to search for a pattern in the forward direction and ? for a pattern in the backward direction. Press Q to quit.
tailUsed to print the last 10 lines of a file by default. You can change the number of lines by doing -n 15 or just -15 if you wanted to look at the last 15 lines instead of the default.
headThe opposite of tail; by default it prints the first 10 lines of a file.

Update the OS packages


sudo apt-get update
sudo apt-get upgrade
sudo apt-get dist-upgrade
sudo reboot



Use Pipes

The UNIX/Linux philosophy is to have many simple and short programs (or commands) cooperate together to produce quite complex results, rather than have one complex program with many possible options and modes of operation. In order to accomplish this, extensive use of pipes is made; you can pipe the output of one command or program into another as its input.

In order to do this we use the vertical-bar, |, (pipe symbol) between commands as in:
$ command1 | command2 | command3


The above represents what we often call a pipeline and allows Linux to combine the actions of several commands into one. This is extraordinarily efficient because command2 and command3 do not have to wait for the previous pipeline commands to complete before they can begin hacking at the data in their input streams; on multiple CPU or core systems the available computing power is much better utilized and things get done quicker. In addition there is no need to save output in (temporary) files between the stages in the pipeline, which saves disk space and reduces reading and writing from disk, which is often the slowest bottleneck in getting something done.