Multi-level ternary operator
Love one. Can’t be easily debugged, probably .. But reads very nicely:
String getScmClass() { ( this.@scmClass == 'svn' ) ? 'hudson.scm.SubversionSCM' : ( this.@scmClass == 'git' ) ? 'hudson.plugins.git.GitSCM' : null }
Still, standard mapping is usually better – it allows to dump all known options:
Map scmClasses = [ svn : 'hudson.scm.SubversionSCM', git : 'hudson.plugins.git.GitSCM' ] String getScmClass() { def scmClass = scmClasses[ this.@scmClass ] assert scmClass, "Unknown [${this.@scmClass}]. Known classes are ${scmClasses.keySet()}" scmClass }
Moving to a private WordPress hosting
I think I will be moving to a private WordPress hosting within time.
The problem with WordPress.com is that it:
- .. is annoyingly slow oftentimes.
There were cases when “Update” operation just stuck for no reason and I had to repost
the whole article. - .. doesn’t allow to install any custom plugin, theme or widget
I mean, it’s good as a free service but from all SaaS-es I’ve met recently – this one definitely needs to go more personal, so I’m giving DreamHost a try now. Any other recommendations may be?
But, of course, I wouldn’t like to lose WordPress.com community and auto-generated links service so I’m planning to repost everything here from my personal WordPress setup.
Using Groovy++ with Maven – an update
In his comment to “Using Groovy++ with Maven” Joern Schimmelpfeng has correctly pointed out about transitive dependencies that may be added to groovypp
POM, eliminating the need to specify them explicitly.
He’s right! That’s what we have Maven dependencies mechanism for.
It is now fixed so you can use
<dependency> <groupid>org.mbte.groovypp</groupid> <artifactid>groovypp-all</artifactid> <version>0.2.0</version> </dependency>
or
<dependency> <groupid>org.mbte.groovypp</groupid> <artifactid>groovypp</artifactid> <version>0.2.0</version> </dependency>
Full examples:
groovypp-all
versiongroovypp
version
Now it looks practically the same – what’s the difference then?
As previously, groovypp-all.jar
contains all required libraries, repackaged in one jar:
In addition to groovy
("groovy"
, "org.codehaus.groovy"
packages) and groovypp
("org.mbte.groovypp"
package) it also contains antlr
, asm
, commons-cli
and junit
libraries, some of which are stored under modified package names to avoid collisions with libraries that may be available already in your project.
groovypp.jar
is bound to all above libraries in separate jars, as declared by its <dependencies>
.
Which one to choose then?
- If your application has already declared
<dependency>
on eitherantlr
orasm
and you
don’t like them packaged twice (note, even in this case – there are no collisions due to
packages modified), then you can choose thegroovypp
version.
It usually works better in IDEA project as it doesn’t recognize"groovypp-all"
jar as
Groovy and tries to search for it elsewhere.
Note: sometimes you may need to add an explicitjunit:4.7
dependency, otherwise
Maven brings old 3.8.2 JUnit version lacking classes required for Groovy++ compilation.
It can be seen ingroovypp
example above, see the diff of switching from"groovypp-all"
to"groovypp"
.
- If you just want to just use Groovy++ without dealing with JUnit versions and you
experience no Groovy-related IDEA problems – you can usegroovypp-all
.
Git GUI Here
Rather than right-clicking the folder and choosing “Git GUI Here” ..
"git-gui.bat":
@echo off start d:\winny\Git\bin\wish.exe d:\winny\Git\libexec\git-core\git-gui --working-dir %*
assuming msysgit is installed at "d:\winny\Git"
Now, I only need to type "git-gui ."
to launch it:
Much better. Hate it when someone forces me to use a mouse ..
Using Groovy++ with Maven
Note, an update is available.
Groovy++ artifacts are now deployed in Maven repository at:
http://groovypp.artifactoryonline.com/groovypp/libs-releases-local/
and
http://groovypp.artifactoryonline.com/groovypp/libs-snapshots-local/
So you can compile your Groovy++ sources with Maven.
As in Groovy, where you pick up either groovy.jar or groovy-all.jar – you can do the same here:
- Use groovy + groovypp + asm + antlr + commons-cli:
<dependency> <groupid>org.codehaus.groovy</groupid> <artifactid>groovy</artifactid> <version>1.8.0-beta-1-SNAPSHOT</version> </dependency> <dependency> <groupid>org.codehaus.groovy</groupid> <artifactid>groovypp</artifactid> <version>0.1.18</version> </dependency> <dependency> <groupid>asm</groupid> <artifactid>asm-all</artifactid> <version>3.2</version> </dependency> <dependency> <groupid>antlr</groupid> <artifactid>antlr</artifactid> <version>2.7.7</version> </dependency> <dependency> <groupid>commons-cli</groupid> <artifactid>commons-cli</artifactid> <version>1.2</version> </dependency>
- Use groovypp-all:
<dependency> <groupid>org.codehaus.groovy</groupid> <artifactid>groovypp-all</artifactid> <version>0.1.18</version> </dependency>
As you see, the second way is much simpler so I suggest you stick to “groovypp-all”. It’s the same as a longer version but with all required libraries packaged nicely in one bigger jar.
Of course, you’ll need to instruct Maven about repository:
<properties> <repo>http://groovypp.artifactoryonline.com/groovypp</repo> </properties> <repositories> <repository> <id>libs-releases</id> <url>${repo}/libs-releases</url> </repository> <repository> <id>libs-snapshots</id> <url>${repo}/libs-snapshots</url> </repository> </repositories> <pluginrepositories> <pluginrepository> <id>plugins-releases</id> <url>${repo}/plugins-releases</url> </pluginrepository> <pluginrepository> <id>plugins-snapshots</id> <url>${repo}/plugins-snapshots</url> </pluginrepository> </pluginrepositories>
.. and configure GMaven plugin:
<plugin> <groupid>org.codehaus.gmaven</groupid> <artifactid>gmaven-plugin</artifactid> <version>1.2</version> <executions> <execution> <id>compile-groovy</id> <phase>process-sources</phase> <goals> <goal>compile</goal> </goals> <configuration> <providerselection>1.7</providerselection> <verbose>true</verbose> <debug>true</debug> <stacktrace>true</stacktrace> <sources> <fileset> <directory>${project.basedir}/src</directory> <includes> <include>**/*.groovy</include> </includes> </fileset> </sources> </configuration> </execution> </executions> <dependencies> <dependency> <groupid>org.codehaus.gmaven.runtime</groupid> <artifactid>gmaven-runtime-1.7</artifactid> <version>1.2</version> <exclusions> <exclusion> <groupid>org.codehaus.groovy</groupid> <artifactid>groovy-all</artifactid> </exclusion> </exclusions> </dependency> <dependency> <groupid>org.codehaus.groovy</groupid> <artifactid>groovypp-all</artifactid> <version>0.1.18</version> </dependency> </dependencies> </plugin>
Full examples:
If you didn’t do so yet – you’re welcome to join a community to take part in Groovy++ discussions and be notified about new releases (@groovypp is available as well).
Also, you can follow Alex Tkachman who is Groovy++ inventor at @alextkachman and DZone.
Artifactory Power Pack – is it really powerful ?
We have been using Artifactory in Thomson Reuters (ClearForest) for more than a year now.
My first Maven repository manager was Nexus – we were using it in my previous workplace. When I came to Thomson Reuters and started working on new CM infrastructures – I decided to switch to Artifactory, though. Mostly due to its richness of features and for supporting an efficient checksum-based storage model for binaries, which was the biggest difference for me between the two products.
We started with version 1.3 and then went through all major upgrades: 2.0, 2.1 (it was a big update: new searches, artifacts metadata, add-ons, and move/copy operations on artifacts). We’re now running version 2.2.1 with Add-ons Power Pack.
To tell you the truth – we really love it as it worked perfectly through the whole period (except in some cases where support and solutions were provided on the same day!).
Within time, I’ve also learned to appreciate how Artifactory goes away from being Maven-only repository manager and becomes a general-purpose storage manager for any kind of binaries and build system. Ivy and Gradle support (with new collaborative relationship just announced) is already available and it’s a really good start. Integration with Hudson and TeamCity (will be available soon) also comes very handy. In fact, one can store any kind of binaries in Artifactory, not related to either Maven or Java in any way!
In short, Artifactory knows (and willing) to cooperate with all major players on today’s arena and that’s a really impressive achievement for something that started as a free-time Maven repo manager project. Well done, guys!
But my particular interest in the last months was it’s Power Pack offering.
We run Hudson pretty intensively as our CI server so when JFrog-ers announced they have a special “Hudson support” – they certainly had my attention!
Now as we have it installed I’d like to see .. Does it matter? I mean, is it really that powerful? The short answer is yes, it does and yes, it is. There’s no doubt about it – see below.
To start with – Power Packs offers different things and even with all our appreciation to Artifactory we’re not using all of them. There’s simply no need for us to right now.
But what we do use is really saving us time (and, therefore, money) on a daily basis:
- Hudson integration – screencast, wiki, blog post
- Properties – screencast, wiki
- Smart searches – screencast, wiki, blog post
- Watches – screencast
Now, let’s take each of them apart.
Hudson integration
This one is definitely the best and draws the most attention, for obvious reasons. The idea is both simple and ingenius – let’s ask CI server (Hudson/TeamCity/Bamboo) to push all build environment data to Artifactory! After all, when a build job is running – it has all environmental information one can think of : OS type and version, JVM version, modules built, their dependencies and versions … Until today all this information was buried somewhere
in Hudson logs and deleted, eventually (I mean, we do need to clean up our build logs sometimes, don’t we?)
Not any more – Hudson integration establishes a bi-directional link between Artifactory and Hudson for each job run. Finally, those two start talking to each other!
How it works:
- Hudson Artifactory plugin is installed
- Hudson is configured and Artifactory server is added
- Hudson job is configured to run "mvn clean
deployinstall"
That’s right, we’re not using maven-deploy-plugin any more - Hudson job is configured to deploy to Artifactory server (specified previously)
and one of repos available – a nice drop-down list allows to choose it:
When (and if!) job finishes successfully – all artifacts archived during the build (<archivingDisabled> should be set to “false” in job’s “config.xml” but that’s a default value) will be deployed by Hudson to Artifactory in one go:
It’s not truly atomic (if the process fails in the middle for some reasons – my guess is nothing would be un-deployed) but it’s still much better than what Maven does by default: deploying each artifact the moment it is ready (so if build process fails in the middle – some newer artifacts would be deployed already while some would stay in the previous version).
As you see, in this sense – Hudson’s way of deploying to Artifactory is much better as it only starts when the build has finished successfully. On top of that, for some weird reason Maven’s traditional deploy has let me down recently with:
Error installing metadata: Error updating group repository metadata
The requested operation cannot be performed on a file with a user-mapped section open
Seems to be some corruption issue that I couldn’t solve.
But "Ok" – I thought to myself – "One more reason not to use ‘mvn deploy’"
Now, does it scale?
After all, Hudson needs to deploy all created artifacts – what if there are too many of them?
In our case, it scales pretty well and there’s no problem whatsoever – our biggest job is publishing 170+ artifacts this way and it works just fine.
Ok, so what else does it actually do?
A lot. First of all, you now have a link to Artifactory in Hudson’s job:
Once we follow it to Artifactory – we get to a page where all build environmental data is stored:
So we have a "Properties" section here with JVM and OS versions recorded (though I wish there were some more), a link back to the Hudson job (I told ya those two started talking to each other!) and, most importantly, "Published Modules":
For each published module – all its dependencies are recorded as well if we ever need to go back in time and figure out what dependencies do we need to re-create the module:
Following "Show In Tree" link we come to the usual artifact’s location in one of our repos where there’s now a new "Builds" tab:
As you see – we now have an exact, bi-directional and traceable information about all jobs that ever deployed our artifacts.
And, like I said, I think it’s a lot. Since now we know for each artifact how it ended up being in Artifactory, by whom and when. We know which job has created it and we know what else was published by this job. For me it’s like a difference between my dady’s old garage (where you can find everything but nobody has any idea how things came along) and my mom’s kitchen (where every little thing has an origin and owner).
Don’t you love it already?
Properties
Historically, Maven doesn’t add much information to an artifact when it’s deployed. Nor does it offer any way to do so. Of course, a certain amount of metadata is added to each *.jar created (like it’s original POM) and each artifact has a traditional
<groupId>:<artifactId>:<version>:<classifier> coordinates (which is a huge improvement since Ant, if we really want to look back for a moment).
But, unfortunately, it only goes so far.
What if we want to mark or tag or label (pick up your favorite name) an artifact ?
A group of artifacts?
How about setting a "product=true" property to those artifacts that are final products (and not intermediate jars) ? We may talk a lot about artifacts and things but after all – people need working products, right? Those having "qa.status=passed" label on them. Or at least "qa.status=ok", may be.
As we can "label" e-mails in Gmail (surprisingly, some people don’t – I think they don’t know what they’re missing) or "tag" Delicious links – I would love to do the same with artifacts!
Some of them I would like to label manually, like QA steps in product lifecycle:
"qa.status = New => Accepted => Rejected => Passed => Graduated (?)"
Other properties I would like to be set automatically, when artifact is deployed: "build.number=35", "product=true" (if artifact is a ZIP file), "jvm=1.6" and the like.
Not surprisingly, there are two ways to set properties in Artifactory:
- Manual
- Automatic
The manual process is demonstrated here and, basically, it goes like this:
- Define a property set: qa.status, qa.version, qa.anything
- Choose a possible value for each property: any value, single-select, multi-select
- Update your repo definition to make this property set available for it
Watch out! If you miss this step – it will not work (happened twice to me) - For any artifact or folder in the tree – go to the new "Properties" tab and add a property
I agree, it’s more similar to Outlook “categories” (than to Gmail labels) and is a little bit involved but .. ok, that’s how it works for now. May be it’ll improve.
Anyway, being a software developer for life – I’m naturally more interested in things happening automatically. So how do I set a property on artifact during the build process?
I want to specify a POM <property> that will become an Artifactory property!
The answer is matrix-params:
<distributionManagement>
<repository>
<id>qa-releases</id>
<url>http://srv/artifactory/qa-rel;buildNumber=${number};rev=${rev}</url>
</repository>
</distributionManagement>
As you see, it is simply a pair of arguments added to the deployment repo definition. Their values are usually taken from regular Maven properties and can be updated by any POM.
For example, to implement our “Products vs rest of artifacts” vision – all we need to do is to add a "product=${product}" matrix param:
<distributionManagement>
<repository>
<id>qa-releases</id>
<url>http://srv/artifactory/qa-rel;product=${product}</url>
</repository>
</distributionManagement>
Some top-level <parent> POM will have it set to "false":
<properties>
<product>false</product>
</properties>
.. but those POMs packaging a final product will have it set to “true”:
<properties>
<product>true</product>
</properties>
Can it be any simpler than that?!
Here’s a blog post demonstrating the same technique for the purpose of artifacts staging and promotion through tagging.
Today, there’s one problem here, though – if we use Hudson integration and switch to "Hudson deploy" (see above) – this <distributionManagement> tag isn’t worth a lot, is it?
And there’s no way to set up any matrix params from Hudson job configuration, where deployment repo is specified. The workaround is simple, though – one just needs to edit job’s “config.xml” (.hudson/jobs/JobName/config.xml) file manually and restart Hudson or “Manage Hudson” => "Reload Configuration from Disk":
<publishers>
<org.jfrog.hudson.ArtifactoryRedeployPublisher>
<details>
<artifactoryName>http://srv/artifactory</artifactoryName>
<repositoryKey>qa-rel;product=${product}</repositoryKey>
</details>
<deployArtifacts>true</deployArtifacts>
<username>..</username>
<scrambledPassword>..</scrambledPassword>
</org.jfrog.hudson.ArtifactoryRedeployPublisher>
</publishers>
The bug is opened so I’m sure it’ll be fixed soon.
Ok, so we have our properties (tags, labels) set – now what? How do we use them?
That’s exactly what the next slide is about …
Smart Searches
In Gmail, searching for labeled mails is a matter of typing "g+l+label" (btw, I much preferred the “Labs” version over the “graduated” one). In Artifactory it’s a little bit involved (again) but that’s due to the fact that Artifactory searches are much more capable.
I believe options provided today would satisfy the most demanding (and esoteric) "querist":
- Quick Search
- Class Search
- GAVC Search
- Property Search
- POM/XML Search
The first three are pretty obvious and very helpful indeed. I use GAVC Search most of the time, and a Class Search occasionally. But it still amazes me how fast Artifactory scans through its indices to locate all instances of, say, Scanner class:
It is a new Property Search we’re after today – it allows combining a query composed of a number of properties.
Like searching for all artifacts where "qa.version=1.0.1" and "qa.status=In QA".
Or, simply put, “What’s being checked today for the upcoming ‘1.0.1’ release?”
It doesn’t matter how properties were set (either manually or automatically) – we can search for all of them! For example, "build.name" and "build.number" are sent by Hudson automatically so we can search by "build.number" as well:
Search results can also be added or subtracted from each other – this is useful when they need to be either expanded or filtered with additional queries. They can also be saved for later use to perform a single operation on all results, where options are “Move”, “Copy” and “Delete”.
The blog post I’ve mentioned already shows exactly that – how artifacts can be
- Searched for
- Promoted to another repository with "Move" / "Copy" operation
As you see, using Property Search anyone can find what he’s looking for (assuming properties were set in the first place, of course): be it a QA person, looking for the last binaries to download and test or a Dev manager, looking for the binaries being QA-ed now.
The last advanced search is POM/XML Search allowing to search through all POMs (in all or specific repos) with XPath queries. I’ve used it yesterday trying to find out which POMs were using some specific plugin. Normally, I just run a textual search on “pom.xml” files through the whole “trunk” and it takes .. well, quite a while, of course. With Artifactory – it can be done smarter and faster:
As you see, "/project/build/plugins/plugin/artifactId" search does the job.
And, of course, it takes less time than an old-school Total Commander textual search
(I don’t even need to measure it – it’s seconds vs minutes!)
So far I didn’t encounter a case where Artifactory searches were not sufficient.
They’re always smart (though I would call it "capable") enough.
Watches
I suppose this one is the easiest to describe and, in fact, there’s probably no need to describe it at all. One can set up “watches” to be notified by e-mail when certain repository, folder or artifact has a “create” or “delete” operation performed on it:
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
The following events have recently occurred in Artifactory on items you are watching:
Sun Jan 10 06:30:21 IST 2010 [user-name/XXX.XXX.XXX.XXX] [CREATED] libs-snapshots-local:com/clearforest/ProductsPage/8.0-SNAPSHOT/ProductsPage-8.0-SNAPSHOT.pom
Sun Jan 10 06:30:18 IST 2010 [user-name/XXX.XXX.XXX.XXX] [CREATED] libs-snapshots-local:com/clearforest/ProductsPage/8.0-SNAPSHOT/ProductsPage-8.0-SNAPSHOT.war
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Ironically, about a year ago – I was practically dying to get this notification: somehow, trunk POMs were overridden by an older versions and I suspected someone from the dev team running “mvn deploy” on an outdated sources (but this was not the case, actually).
So although I get quite a lot of e-mails from my “watches” – I just keep them in case anything like that will ever happen again. And of course, it becomes even more necessary when certain repos are used for special purposes, like in staging and promoting scenario.
Conclusions
I think “Power Pack” is a critical add-on to what Artifactory offers.
Being able to integrate it with Hudson, set custom properties and search for them is what makes it a nice, organized and watched storage rather than a kitchen sink of everything that happened to be downloaded from somewhere on the Internet.
Whether you have it or don’t – Artifactory surely delivers! But the questions are:
- How aware are you of what’s happening?
- How easy it is for you to dig through the mess and find what you need?
May be it’s just me, but I just love knowing what’s going on and when. It keeps me in control of things and not the other way around which I believe is a good thing, in general.
Happy building!
evgeny-goldin.com reloaded
Just uploaded a new design to evgeny-goldin.com.
Finally! (the previous one was just horrible)
Design was made by Inbar (http://imba.co.il/) and Sagit (http://www.sagit.co.il/) – a big THANK YOU, ladies!
Hashcode, Adler, CRC collisions in action!
If you want to see String’s hashcode
, CRC32
or Adler32
colliding (producing same results for different Strings) – run this Groovy script:
import java.util.zip.Adler32 import java.util.zip.CRC32 import java.util.zip.Checksum def checksum( String s, Class<? extends Checksum> c ) { Checksum cs = c.newInstance(); cs.update( s.getBytes(), 0, s.size()) cs.getValue() } assert checksum( "194.153.241.7", Adler32.class ) == checksum( "64.229.15.206", Adler32.class ) assert checksum( "maxtnt05-489.phlpa.fast.net", CRC32.class ) == checksum( "bzq-218-115-93.red.bezeqint.net", CRC32.class ) assert "adsl-67-64-91-128.dsl.austtx.swbell.net".hashCode() == "h98s18a80n47.user.nortelnetworks.com".hashCode() // A combination of two? assert checksum( "/ongoing/pie/0.2/?N=D?N=A?N=A?N=A?D=A?S=A?M=A", Adler32.class ) == checksum( "/ongoing/pie/0.2/?N=D?M=A?S=A?D=A?N=A?N=A?N=A", Adler32.class ) assert "/ongoing/pie/0.2/?N=D?N=A?N=A?N=A?D=A?S=A?M=A".hashCode() == "/ongoing/pie/0.2/?N=D?M=A?S=A?D=A?N=A?N=A?N=A".hashCode()
Those functions aren’t supposed to generate unique numbers, of course (that’s what we have strong hash functions for).
Just thought I’d like to publish some of those collisions ..
turn off buzz
It’s good there’s a “turn off buzz” link at the bottom of my Gmail.
It amazes me how much effort is thrown to make us spend as much time as possible performing pointless and non-productive activities .. Watch TV, commercials, sitcoms, popcorns, browse social networks (Одноклассники!, ВКонтакте!, Facebook!, ЖЖ!), follow everybody and see what they have to say, post comments to their photos, post your own photos, music, video … what a classic examples for Q4 in Covey’s Four Quadrants!
Yeah, sure, part of it is socializing but the way I see it is a waste of time.
May be it’s just me, but I don’t need to socialize that much, really. Friends of mine? I talk to them in person. Others? How many of them have anything valuable to say? Anything worth spending time reading? I mean, I have tons of very smart blogs, articles and books unread (and videos unwatched) to waste my time on this?
My time management is terrible (and I always love my job a little bit too much to leave workplace at normal hours) but I work on improving it and concentrating on things that are important to me. People that are valuable to me. Things that need to be done.
So I turned off all messengers long time ago (I only open Skype or Google Talk to talk to someone). I stopped visiting those stupid social networks long time ago as well. But I loove Twitter – it’s all about news, quick updates and following only those you really want to hear from. And it doesn’t have this annoying “unread” counter (what a piece of brilliance!), pushing one to spend more and more time reading, like, everything – but does one need to?
Buzz? I still don’t get Wave and I don’t get how people find it usable or useful. When it changes – I’ll be glad to join, like it already happened with Zoho. Honestly, there were times when I was laughing at idea to put Word on-line but .. I’m now a big time Zoho user. Well, things change.
So may be one day I’ll “get” what for do I need Wave or Buzz. Till then – turn off buzz and all I want is my Gmail account to load as fast as possible. I appreciate Google’s efforts spent on making Web move faster. But not on making me read more pointless data.
When I leave office in time and get to reading – I have plenty of resources to start from, thank you.
WinKey alternative – the search is over! Clavier+
I’ve mentioned in the past how WinKey is slowly dying on newer Windows versions. This small but highly powerful Win+X shortcuts launcher was one of my most favorite ones for a long time ..
Win + F2 – Total Commander
Win + N – notepad
Win + Down – minimize the window
Win + Q – Firefox
Unfortunately, it’s support and development was abandoned by Copernic (I remember seeing a page on their site talking about that) so it started malfunctioning on Windows 7 RC that I’m running at home .. On Windows 2008 64 bits I couldn’t even install it.
Obviously, the time has come to find a replacement.
As I expected, it wasn’t easy – there are zillions of various launchers available but none of them supported simple Win+X shortcuts. Some required clicking with mouse (come on, my hands are already on the keyboard – I don’t want to take them away to grab a mouse just to launch an application!), some were too complex, some were plain ugly.
I think RocketDock, True Launch Bar, and Launch-n-Go were the best candidates but ..
I was looking for an exact WinKey replacement and none of them could deliver that.
Eventually, it struck me to not to search for “keyboard shortcut launcher” but for “WinKey alternative” which brought HoeKey and Clavier+ right away.
Of those two – Clavier+ is definitely the best option:
All right !.. I get my Win+N back. What a relief, really.
It also allows to paste a block of text so with Ctrl+Shift+E I can quickly paste my work e-mail which is quite handy, of course (sending e-mails to myself is something I do a lot)