Artifact Size. A story of size enforcement.

So it looks like I’ve not been posting here for quite some time. Last post was in 2017…

The maven enforcer on the binary size can be a very useful tool as well as disruptive and close to useless. I think, in this world of cloud deployment and containers it’s getting again useful as you want a microservice to be as little as possible and be aware if it breaks a certain limit.

I said it: a microservice. What if you are working on a HUGE monolith comprised by 185 different OSGi bundles and God only knows how many other form of binary content are distributed in there?

In this last case, you may have a limit, with some margin and then the size is creeping closer and closer to the limit. Then you send your PR, which increase the size by just 2KB and BANG! Fails the check. WTF! You sure think… not my fault. Why on earth… regardless.

The point now is to find if there has been any commit which pushed the size considerably in the “recent past”. A task that done by hand can be tedious and error prone. So here’s another script. With a very imaginative name: artifact-size.sh.

What it does is fairly simple. it takes 3 mandatory parameters: repo, name and numer of commits. Repo is the path to a local git clone of the project, name is the relative path to the generated artifact (the jar) and number of commits is, well…, the number of commits you want to go back in time.

Don’t worry, if just invoke it without any parameter it will give you a help.

It will then take that number of commits, go back in time and start working forwards one commit a time packaging your project and collecting in target/ a CSV with the meaningful info like: commit hash, size and something more. You can then open this CSV file in open office (for example) and start playing with filters, putting it through a complex pipe of bash command or, why not, read it as it is.

If you’re lucky like me, you can find a single commit pushing considerably the size (20MB+) and therefore can work with the developer on what can be done (reverting?).

Potential future improvements? Here are some ideas and PR are always welcome

  • get a % of increase as argument and flag already any commit that goes beyond such threshold.
  • take the above % and work with bisect pinning down the first commit that bumped the size beyond such threshold.

https://github.com/davidegiannella/misc/blob/master/artifact-size.sh

Maven release plugin and local changes

It may happen that you’re trying to fix some issues that affects the release of a maven project and therefore you apply some changes locally and then run

mvn release:prepare -DdryRun=true -Darguments=-DskipTests

Then you get an error message like

[ERROR] Failed to execute goal org.apache.maven.plugins:maven-release-plugin:2.5.3:prepare (default-cli) on project jackrabbit-oak: Cannot prepare the release because you have local modifications :
[ERROR] [oak-api/src/main/java/org/apache/jackrabbit/oak/api/PropertyState.java:modified]
[ERROR] [oak-api/src/main/java/org/apache/jackrabbit/oak/api/jmx/CacheStatsMBean.java:modified]
[ERROR] [oak-api/src/main/java/org/apache/jackrabbit/oak/api/jmx/CheckpointMBean.java:modified]
[ERROR] [oak-api/src/main/java/org/apache/jackrabbit/oak/api/jmx/IndexStatsMBean.java:modified]
[ERROR] [oak-api/src/main/java/org/apache/jackrabbit/oak/api/jmx/RepositoryStatsMBean.java:modified]

Sure. release-plugin is giving you an extra check about any local changes that may not end in the release. However you don’t want to commit those changes to the SCM just yet; you want first to see if they works.

A “quick” workaround is on the command line in the (verbose) form of

mvn release:prepare -DdryRun=true -Darguments=-DskipTests -DcheckModificationExcludeList=oak-api/src/main/java/org/apache/jackrabbit/oak/api/*,oak-api/src/main/java/org/apache/jackrabbit/oak/api/jmx/*

You can as well specify something in the poms directly however I never had the need.

References:

 

CQ5/OSGi reference a unique service implementation

You should never ever do it. If you find yourself in the need of this; there’s something extremely wrong in your code. Nevertheless I found myself in needing it for some old legacy code that was almost impossible to fix in a reasonable time.

The question is: how can I reference a specific implementation of a service/component in an OSGi (therefore CQ5 as well) environment?

In the component, where you need to reference the implementation you can specify something like the following

@Reference(target="(component.name=com.foo.BarImpl)")
Bar bar;

In the component implementing the service you’ll have something like

@Component(immediate=true, metatype=false, name="com.foo.BarImpl")
@Service
class BarImpl implements Bar{
...

By default the framework will assign the fully qualified class name as component name. I prefer to specify it for making the code more readable and no one prohibit you to specify any arbitrary string like mickey mouse or goofy as component name.

Reference:

Findings of eclipse with testng

First of all I’ve always used JUnit and not TestNG. This post is not blaming on anything. It’s just pointing out the little stuff I had to do to make my eclipse works with TestNG.

  1. Install TestNG from eclipse marketplace and restart
  2. The @Test annotation has to be on it’s own line of the plugin won’t pick it up
  3. In assertEquals() (and I’m expecting all the others) expected vs actual values are actually exchanged
  4. You have to configure the Output Folder of eclipse to go under $project/target/target-classes or eclipse won’t pick it up.

That’s it for now.

 

Removing the design icon from SideKick in CQ5

When you set-up a proper website management in large organisations you’ll end up in having two main groups: designers and content editors.

Normally you want the designers to be a more powerful content editors; with added functionality like selecting which components you can use for contributing the pages.

In order to remove the “design icon” from the Sidekick for a specific group/user you simply have to act on ACL and remove everything but Read permissions on /etc/designs.

Having so

               Read Modify Create Delete R/ACL E/ACL Repl.
/etc/designs    V     x      x      x     x      x    x

CQ5 Maven Archetype

Add the Adobe profile in your ~/.m2/settings.xml

    <profile>
      <id>adobe-public</id>
      <activation>
        <activeByDefault>false</activeByDefault>
      </activation>
      <properties>
        <releaseRepository-Id>adobe-public-releases</releaseRepository-Id>
        <releaseRepository-Name>Adobe Public Releases</releaseRepository-Name>
        <releaseRepository-URL>http://repo.adobe.com/nexus/content/groups/public</releaseRepository-URL>
      </properties>
      <repositories>
        <repository>
          <id>adobe-public-releases</id>
          <name>Adobe Basel Public Repository</name>
          <url>http://repo.adobe.com/nexus/content/groups/public</url>
          <releases>
            <enabled>true</enabled>
            <updatePolicy>never</updatePolicy>
          </releases>
          <snapshots>
            <enabled>false</enabled>
          </snapshots>
        </repository>
      </repositories>
      <pluginRepositories>
        <pluginRepository>
          <id>adobe-public-releases</id>
          <name>Adobe Basel Public Repository</name>
          <url>http://repo.adobe.com/nexus/content/groups/public</url>
          <releases>
            <enabled>true</enabled>
            <updatePolicy>never</updatePolicy>
          </releases>
          <snapshots>
            <enabled>false</enabled>
          </snapshots>
        </pluginRepository>
      </pluginRepositories>
    </profile>
  </profiles>

Creating a multi-module project:

mvn archetype:generate -DarchetypeGroupId=com.day.jcr.vault \
 -DarchetypeArtifactId=multimodule-content-package-archetype \
 -DarchetypeVersion=1.0.0 \
 -DarchetypeRepository=adobe-public-releases \
 -P adobe-public

Check about any update to the archetypeVersion.

For not specifying the profile at every mvn command add to the ${project}/pom.xml (the parent one). This is no longer needed with archetype 1.0.2+

   <repositories>
     <repository>
         <id>adobe-ext.hoc</id>
         <name>Adobe External Central Repository</name>
         <url>http://repo.adobe.com/nexus/content/groups/public</url>
     </repository>
   </repositories>
   <pluginRepositories>
      <pluginRepository>
          <id>adobe-public-releases</id>
          <name>Adobe Basel Public Repository</name>
          <url>http://repo.adobe.com/nexus/content/groups/public</url>
          <releases>
              <enabled>true</enabled>
              <updatePolicy>never</updatePolicy>
          </releases>
          <snapshots>
              <enabled>false</enabled>
          </snapshots>
      </pluginRepository>
   </pluginRepositories>

In ${project}/pom.xml check the content-package-maven-plugin pluging version. For example the content-package plugin in the archetype is 0.0.13 but as at 10th September 2012 the latest one is 0.0.19

To install everything execute from the root project

sampleapp$ mvn install -P autoInstallPackage

To install only the bundle

sampleapp/bundle$ mvn sling:install

To install only the content package

sampleapp/content$ mvn install -P autoInstallPackage

Adding some exclude filtering to the contet package to avoid any spurius includes.

In sampleapp/content/pom.xml replace the exclude section with the
following

<excludes>
    <exclude>**/.vlt</exclude>
    <exclude>**/.vltignore</exclude>
    <exclude>**/*.iml</exclude>
    <exclude>**/.classpath</exclude>
    <exclude>**/.project</exclude>
    <exclude>**/.DS_Store</exclude>
    <exclude>**/target/**</exclude>
    <exclude>libs/**</exclude>
</excludes>

Optional step

Personally I don’t like a package name like “sampleapp-content-x.y.z”
as I’m actually deploying a codebase and not content. So I find it a
bit misleading. In order to have the package named as
“sampleapp-codebase-x.y.z” in the sampleapp/content/pom.xml in the
build->plugin->configuration for the content-package-maven-pluing add
the name and finalName definition as following

<plugin>
  <groupId>com.day.jcr.vault</groupId>
  <artifactId>content-package-maven-plugin</artifactId>
  <extensions>true</extensions>
  <configuration>
    <name>sampleapp-codebase</name>
    <finalName>sampleapp-codebase</finalName>
    <failOnError>true</failOnError>
...

Maven import

if you’re using CQ 5.5+ you can avoid checking and importing each dependency version by using the com.day.cq:cq-quickstart-product-dependencies dependency in parent pom.

JSP Support in Eclipse

Don’t forget to add the JSP support for eclipse by following: How to work with JSP.

In eclipse then by converting the project to a faceted, dynamic web project and with the proper context root path you’ll have the proper JSP support. Don’t forget to delete the rubbish that eclipse puts in when converting to a dynamic web project.

Screen Shot 2013-10-16 at 12.35.55

Screen Shot 2013-10-16 at 12.36.14

References

http://dev.day.com/docs/en/cq/current/core/how_to/how_to_use_the_vlttool/vlt-mavenplugin.html

CQ & Sling servlets: resourceType

Last time we see how to use Sling servlets with old legacy URL. Today we’ll see how to create Sling Servlets in the Sling-way: using resourceType.

Scenario

We want a component that can be dragged into a parsys. With a form that submit data to a sling servlet. Then the servlet will do the operations required.

Process

Let’s create the component following the CQ guidelines for making it drag-droppable into a parsys (allowedParents): geometrixx/components/test003. The test003.jsp will look like the following:

<%@include file="/libs/foundation/global.jsp"%>
<form name="frm" method="post" action="<c:out value="${resource.path}" />.c">
    <input type="submit" />
</form>

This JSP will answer at a GET http call showing the form. In order to use, create a content page and drag the component into a parsys.

Now it’s time to create the servlet. As usual you’ll have to register it using some felix annotations. The following servlet will be used when posting (HTTP POST) to a resource of type geometrixx/components/test003 with an extension of “c”.

package com.samples.sling;

import java.io.IOException;
import javax.servlet.ServletException;
import org.apache.sling.api.SlingHttpServletRequest;
import org.apache.sling.api.SlingHttpServletResponse;
import org.apache.sling.api.servlets.SlingAllMethodsServlet;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
 * @scr.component immediate="true" metatype="false"
 * @scr.service interface="javax.servlet.Servlet"
 * @scr.property name="sling.servlet.methods" values.0="POST"
 * @scr.property name="sling.servlet.resourceTypes" values.0="geometrixx/components/test003"
 * @scr.property name="sling.servlet.extensions" values.0="c"
 */

public class ResourceTypePostServlet extends SlingAllMethodsServlet {
   private static final long serialVersionUID = 8795673847499208743L;
   private final static Logger logger = LoggerFactory.getLogger(ResourceTypePostServlet.class);

   @Override
   protected void doPost(SlingHttpServletRequest request, SlingHttpServletResponse response) throws ServletException, IOException {
      logger.debug("ResourceTypePostServlet::doPost()");
      response.setContentType("text/plain");
      response.getOutputStream().print("Hello ResourceTypePostServlet World!");
   }
}

We used the sling.servlet.methods to specify which HTTP method the servlet listen to, the sling.servlet.resourceTypes to specify all the resource types is should listen to and the .extensions if you wish to specify an extension.

Remember that if you specify a sling.servlet.paths, resourceTypes, extensions and selectors will be ignored.

Now build the bundle and refresh the previously created content page (should not necessary actually). If you click on the “submit” button you’ll see the Hello World.

References:

CQ & Sling servlets: old legacy URL

SlingIn this short article (first of a serie I hope) I’ll show you how to create a Sling Servlet in CQ.

Scenario

You are migrating your existing application in CQ a piece at time and it’s time for the Servlets (controllers) part, so for the first stage you’d like to keep the old URLs. Maybe because some other external services are calling you at these coordinates or because you don’t want to enter each jsp and change the forms action.

Ingredients

  • CQ instance running in Author (localhost:4502)
  • CRXDE (let’s ease the game for starting)

Process

Create a new OSGi bundle under geometrixx (or your app). In this create a new class that extends the SlingSafeMethodsServlet.
Then you have to register as a service in the OSGi container. This is done using the Felix annotations as following:

/**
 * @scr.component immediate="true" metatype="false"
 * @scr.service interface="javax.servlet.Servlet"
 * @scr.property name="sling.servlet.methods" values.0="GET"
 * @scr.property name="sling.servlet.paths" values.0="/path/to/my/servlet"
 *                                          values.1="/apps/path/to/my/servlet"
 */

I’ll leave you to the references section of this post for getting deeper into the meaning of each.

At the end your code will look something like the following:

package com.samples.sling;

import java.io.IOException;
import javax.servlet.ServletException;
import org.apache.sling.api.SlingHttpServletRequest;
import org.apache.sling.api.SlingHttpServletResponse;
import org.apache.sling.api.servlets.SlingSafeMethodsServlet;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
 * @scr.component immediate="true" metatype="false"
 * @scr.service interface="javax.servlet.Servlet"
 * @scr.property name="sling.servlet.methods" values.0="GET"
 * @scr.property name="sling.servlet.paths" values.0="/path/to/my/servlet"
 *                values.1="/apps/path/to/my/servlet"
 */
public class AbsoluteUrlServlet extends SlingSafeMethodsServlet {
   private static final long serialVersionUID = -1920460619265757059L;
   private static final Logger logger = LoggerFactory.getLogger(AbsoluteUrlServlet.class);

   @Override
   protected void doGet(SlingHttpServletRequest request, SlingHttpServletResponse response) throws ServletException, IOException {
      logger.debug("AbsoluteUrlServlet::doGet()");
      response.setContentType("text/plain");
      response.getOutputStream().print("Hello AbsoluteUrlServlet World!");
   }
}

Looking at the sling.servlet.paths annotation we se on which url we are actually mapping the servlet: /path/to/my/servlet and /apps/path/to/my/servlet.

Building the bundle and CRXDE+felix will do the magic of the deployment.

Now if you try to run localhost:4502/apps/path/to/my/servlet you’ll see our beautiful Helloworld, but if you’ll try to access localhost:4502/path/to/my/servlet you’ll get a 404 or a content listing.
This is due to a settings in felix+Sling. Open the felix console at the configuration tab (/system/console/configMgr) and search for Apache Sling Servlet/Script Resolver and Error Handler. Here you’ll have to add “/path/” to the Execution Paths section.

Done done!

References

Weekly link 2011-08

AeroVironment/DARPA Nano Hummingbird UAV flying

Hello JavaFX 2! – A TableView Component

London Stock Exchange in historic Linux go-live

Microsoft: Absolutely NO (GPLv3-or-compat-licensed) Free Software for Windows Phone and Xbox Apps.

BMW Wants to Be the Ultimate Green Machine

Intel’s Light Peak May See the Light This Week.

Intel details Thunderbolt, says Apple has full year head start

LaCie announce Intel Thunderbolt-equipped Little Big Disk