AbstractJavaScriptComponent @JavaScript annotation issue

I am creating a subclass of AbstractJavaScriptComponent and my @JavaScript annotation to load CKEditor’s javascript seems to require that I specify all subparts or else I get an error.

Normally, to use CKEditor, I just need to reference a single JS file, and that JS will then load any other components that it needs. But my annotation was not enough. I originally tried:

@JavaScript({“ckeditor/ckeditor.js”,“ckeditorForVaadin7.js”,“ckeditor-connector.js”})

That would reference CKEditor itself, and my component script and my connector script. But then I ended up with various errors like:

WARNING: Rejecting published file request for file that has not been published: ckeditor/contents.css
Jun 17, 2013 2:35:42 PM com.vaadin.server.AbstractCommunicationManager servePublishedFile
WARNING: Rejecting published file request for file that has not been published: ckeditor/skins/moono/icons.png

So I started adding in the components listed in the errors, and ended up with:

@JavaScript({“ckeditor/ckeditor.js”,“ckeditor/config.js”,“ckeditor/skins/moono/editor.css”,“ckeditor/lang/en.js”,“ckeditor/styles.js”,“ckeditor/contents.css”,“ckeditor/skins/moono/icons.png”,“ckeditorForVaadin7.js”,“ckeditor-connector.js”})

That appears to now work to load, but of course all of components in the ‘ckeditor/’ path besides the first are parts loaded independently (and the exact contents will change on a per user basis based on what subcomponents they choose, language, skins, etc.) and seem like I shouldn’t have to list them.

Is this a bug, or is the @JavaScript not going to work to load a JS library that itself loads additional parts without listing every possible subpart that it could load?

Is there a solution here? It makes no sense if I have to list every JS, CSS and image file used by a JS library in order for this to work. It seems that the main JS libraries I specify in the @JavaScript notation should be loaded, but all subsequent requests need to go through “as is”.

I’m just guessing because I haven’t run into this myself but you may be trying to include the other resources from your main JavaScript file using relative paths. If you don’t include them in your @JavaScript annotation, Vaadin will not allow access to them because they are not considered public. I can think of three options to try:

Put the JS/CSS files (other than your root one) at some known, public path in your WAR and then just include them directly, outside of the Vaadin servlet path. For example, if your Vaadin servlet was under the context /app/ you might add a /resources/ mapping to serve all your JS and CSS outside the scope of Vaadin.

Another option might be to create your own RequestHandler and server your content out of the Vaadin application as basic HTTP resources. This requires a little more work because you’ll have to directly handle the HTTP request and response but it gives you a lot of flexibility. It is similar to implementing the Servlet interface directly.

Yet another option (and perhaps the easiest), would be to add your resources as ClassResources to your component’s state object via AbstractClientConnector#setResource. These resources should then be visible in the JS as getState().resources..uRL. It took me a while to find this as I couldn’t find it in the documentation but it has worked well so far for including extra files by generating tags in the client side JS.

Hope that helps,
-mike

Thanks for the response, Mike. Well, CKEditor is a big JS library that I had nothing to do with, but it seems quite logical that any JS library would load subsequent resources based on the path where the main code was loaded from.

Well, not sure about this since I don’t actually load any of the other JS/CSS/images. They occur naturally as parts need to be loaded. That is, an icon and CSS for a dialog box are not even downloaded if that dialog box is never opened.

I could perhaps use a fixed path(rather than a relative one in my @JavaScript annotation, but I don’t think that solves things either per this ticket:
http://dev.vaadin.com/ticket/11743

Noted a similar forum complaint as the annotation is not providing the services it needs to provide:

https://vaadin.com/forum#!/thread/2635269
But the solution to add the resource directories would work for me if there’s a general way to do it (I don’t use Maven pom.xml). Listing the individual parts it not possible, but I can certainly list the directories.

I’d consider this, but is there any example of making this work that all sub-resource URLs are served by my code instead?

That may be a big help, though not if it means I have to tweak the JS in the library I’m trying to include, or if I have to list any parts beyond a few directories. The downside is this is a pure JS component and has no AbstractClientConnector and uses a JS-based connector that currently only makes reference to this.onStateChange = function() { … };

I cannot believe that this is not a “normal issue” (bug), though, since many bigger JS libraries include lots of parts at runtime (rather then entire point of modern web UIs). It seems that at the very least I should be able to give a base path as a resource so that all subcomponents in that path become available.

Or, let me point to a fixed location for my JS and then avoid the /APP/PUBLISHED/… scheme entirely.

So let’s say you setup your application like:
/app/* → Vaadin servlet
/ckeditor/* → a public path to files in the war (or servlet, doesn’t matter)

If your JS connector includes a resource like /ckeditor/editor.js, ideally all the resources it includes would be relative to editor.js so everything would load from /ckeditor/ which is publicly accessible. Therefore the only file you need to list in the @JavaScript annotation is your root connector which generates the script tag for /ckeditor/editor.js.

As for using setResource, you’re right that it won’t work if the JS you’re including is itself including other JavaScript files because they wouldn’t be exposed. That being said, your AbstractJavaScriptComponent class is an AbstractClientConnector so you have access to to the setResource method if you need it. The resources will be exposed via the state on the client side in your onStateChange JS callback.

I’d try using a public path in the war or a RequestHandler (they both do basically the same thing) and include the root ckeditor script from that location. Then see if ckeditor properly loads all dependent resources relative to the root script you included. This is getting into standard JS stuff at this point and outside of Vaadin. That is, you could do this test using a static HTML file by just including ckeditor from some path other than where your HTML file is and verify that it loads all resources relative to the root ckeditor script.

-mike

BTW, I’m almost 100% positive that this will work because I wrote a Twitter timeline component a while back that includes JS (and then other resources) directly from Twitter like the code below:


org_mpilone_vaadin_TwitterTimeline = function() {
	var e = this.getElement();

  this.onStateChange = function() {
	  e.innerHTML = "<a class=\"twitter-timeline\" " +
	   "data-dnt=true href=\"https://twitter.com/" + 
	   this.getState().twitterId + "\" data-widget-id=\"" + 
	   this.getState().widgetId + "\">Tweets by @" + 
	   this.getState().twitterId + "</a>";

	  // Include the Twitter JS file.
	  var js=document.createElement("script");
	  js.id="twitter-wjs";
	  js.src="//platform.twitter.com/widgets.js";
	  e.appendChild(js);
  }
}

Thanks again, Mike. Well, I confess that I had a better grasp of Vaadin 6 after a couple of years, but this is a bit mystifying since my CKEditor widget that we did for the Vaadin 7 port (using legacy stuff, not using the “vaadin loves javascript” scheme – and still GWT!) works just fine and I don’t have to specify anything more than the root JS file.

This component is supposed to be reusable (it’s a directory add-on) just by dropping the JAR in WEB-INF/lib. Users shouldn’t have to install CKEditor in any place as it can be served from the JAR. The solution has to be easy for all users of this add-on.

Also, the main editor script is loaded with this notation:

So, it seems that “base URL” will be http://localhost/CKEditorForVaadin7/APP/PUBLISHED/ckeditor/ at best and not pointing to any other location to be served separately. I’m not even sure what the path would be to attempt to retrieve from inside a JAR without going through Vaadin. While I can deploy it outside of the JAR, that makes the add-on much less usable and was never an issue before.

If the GWT side can do it, shouldn’t the JS scheme be doable, too? I hate to abandon this new port to instead go back to a GWT port.

And yes, in the prior GWT-based solution, we did in fact load the JS in regular HTML pages, too, outside of Vaadin. This was easily done by pointing to the URL:
/VAADIN/widgetsets/com.esignforms.open.vaadin.widgetset.Open_esignformsvaadin7Widgetset/ckeditor/ckeditor.js

Somehow in that older mode of serving JS/CSS/images, it had no issue blocking subcomponents. But now this won’t work since I am pretty sure a regular HTML page won’t be able to rely on the the URL: /APP/PUBLISHED/ckeditor/ckeditor.js, at least not in this semi-broken model.

Is there really nobody at Vaadin who know how this is supposed to work in their design? I mean, here’s a big change that must affect any add-on that builds on a substantial JS library that dynamically loads subcomponents. I feel like I missing something simple or there is a fix to be done in Vaadin to allow me to specify a directory resource such that all components inside are made available (including JS, CSS, images).

If you put the static files in a folder inside the VAADIN folder they will be published as is and relative paths will work. See https://vaadin.com/wiki/-/wiki/Main/Packaging%20SCSS%20or%20CSS%20in%20an%20add-on

Since CKEditor loads all sorts of JS, CSS and images (mostly PNG), is there a way I can simulate adding in all of the resources? While I’ve not tried, I may be able to walk the path where the CKEditor code is to gather all of the subcomponents so they can be registered in some way as valid to be served via the /APP/PUBLISHED scheme.

I took a look at AbstractJavaScriptComponent.setResource(String,Resource) (it’s actually part of AbstractClientConnector), but I’m not sure what the “key” values would be and what sort of Resource should I use (ClassResource?). Would that be a programmatic way to add all files in my ‘ckeditor’ subdirectory so they can be served by Vaadin? If so, what would the syntax be since this isn’t it!

setResource(“ckeditor/ckeditor.js”,new ClassResource(“ckeditor/ckeditor.js”));

Still hoping there’s a reasonable solution… Soon I’ll have to abandon trying to make a JavaScriptComponent version if it’s just not ready to be used yet.

Thanks, Artur. This sounds very promising, but my first attempt at it did not succeed. I moved the CKEditor javascript library (all of the JS that’s not part of my add-on coding) to my VAADIN/addons/CKEditorForVaadin7/ckeditor folder. Aside from the “VAADIN” path, is anything else meaningful? Do I have to use ‘addons’? Does the next path have to match my add-on’s name or anything, and if so, what exactly is considered to be the add-on’s name?

My annotation now looks like:
@JavaScript({“vaadin://addons/CKEditorForVaadin7/ckeditor/ckeditor.js”,“ckeditorForVaadin7.js”,“ckeditor-connector.js”})

But the first one is not being loaded, which of course if the main JS for CKEditor. It appears that the request URL is:

Request URL:vaadin://addons/CKEditorForVaadin7/ckeditor/ckeditor.js

So I’m not sure why the ‘vaadin://’ prefix is not converted to a real URL for the browser to request. Clearly I’m not doing this correctly, but it does have promise since I do want all code to be served “as is”.

EDIT: Ok, I take that back. I was looking at master and not the 7.1 branch. It looks like the vaadin:// protocol was added in 7.1 which explains why it won’t work for me in 7.0.x.

ORIGINAL POST:
I also haven’t had any luck getting the vaadin:// protocol working. I did however get a bit of hack URL to work by doing:


@JavaScript({ "mycustomcomponent_connector.js",
    "app://../VAADIN/addons/test_file.js" })

The app:// protocol appears to generate a URL relative to your application (/foo/* in my case) and then the …/VAADIN/ makes a relative path to the public files in the /VAADIN/ directory. I haven’t tried this with the JS file in an add-on JAR, but it seems like it should work.

Artur, are you sure the vaadin:// protocol is implemented? Looking at LegacyCommunicationManager I can see the resolution of the “published://” protocol and looking at the ApplicationConnection I can see the resolution of the “theme://”, “published://” and “app://” protocols, but I can’t find any reference to the “vaadin://” protocol. ApplicationConstants appears to define constants for all the protocols but again, “vaadin://” isn’t listed.

Any suggestions?

-mike

Mike, thank you so much. That did the trick! Maybe in 7.1 we’ll have to switch to ‘vaadin://’ but right now ‘app://’ is working like a charm (or so it seems so far ) in 7.0.7.

I changed my JavaScriptComponent to use:
@JavaScript({“app://VAADIN/addons/CKEditorForVaadin7/ckeditor/ckeditor.js”,“ckeditorForVaadin7.js”,“ckeditor-connector.js”})

And now it’s loading just fine without any blocked attempts to reference subsequent CSS, JS and images. The injected script tag looks as expected, too:

Assuming this works when I package it up and deploy in other webapps, this has RESOLVED my issue. Many thanks for this!

But, oddly enough, I can no longer EXPORT my add-on from Eclipse. It now gets a null pointer exception. Is there any issue with creating the add-on with my own ‘addons’ folder and the rest inside the VAADIN folder? I was able to export the project before I did that move. Is it confusing the Vaadin export wizard to have my own folders there?


!ENTRY org.eclipse.ui 4 0 2013-06-25 10:13:29.034
!MESSAGE Unhandled event loop exception
!STACK 0
java.lang.NullPointerException
	at com.vaadin.integration.eclipse.util.VaadinPluginUtil.findStylesheetsString(VaadinPluginUtil.java:1413)
	at com.vaadin.integration.eclipse.wizards.DirectoryPackageData.setupProject(DirectoryPackageData.java:106)
	at com.vaadin.integration.eclipse.wizards.DirectoryPackageWizard.init(DirectoryPackageWizard.java:184)
	at org.eclipse.ui.internal.dialogs.WorkbenchWizardNode.getWizard(WorkbenchWizardNode.java:185)
	at org.eclipse.jface.wizard.WizardSelectionPage.getNextPage(WizardSelectionPage.java:104)
	at org.eclipse.ui.internal.dialogs.WorkbenchWizardSelectionPage.getNextPage(WorkbenchWizardSelectionPage.java:100)
	at org.eclipse.ui.internal.dialogs.ImportExportPage.getNextPage(ImportExportPage.java:447)
	at org.eclipse.jface.wizard.WizardDialog.nextPressed(WizardDialog.java:908)
	at org.eclipse.jface.wizard.WizardDialog.buttonPressed(WizardDialog.java:428)

Noted that the VAADIN/addons is apparently an established name, but since I don’t have any styles in there, it appears that the latest version of the Eclipse Vaadin plugin gets the null pointer.

I renamed to VAADIN/addon-js and then there was no issue doing the export to create the add-on JAR. It may all be moot, though, since I didn’t realize that my JS component wouldn’t easily function as a Field (for FieldGroup or legacy Form), and that’s really something it ought to do. It’s not clear how easy/hard it would be to implement the Field interface.

I had a similar issue and I found that implementing the Field interface correctly was rather time consuming. To get something up and running quickly I created an abstract class that extends AbstractJavaScriptComponent and implements the Field interface by delegating to an AbstractField. So far it has worked pretty well but I’m sure there are some scenarios where it will break. You might find it useful to get you up and running but don’t be surprised if you end up slowly implementing all the fields methods for real to get everything 100%.

I did the same thing for CustomComponent so I could have a “field” composed of a bunch of other fields to create complex components like a two column select by reusing existing Vaadin fields.

-mike
13075.java (8.87 KB)

Artur, thanks for mentioning this. It was exactly what I needed.

I read this forum and help me a lot to understands whats going on. Today I´ve the same problem but in a different environment and vaadin version (8.3): I want to use CKEditor JS library inside Liferay DXP (i.e., OSGi environment).
I tried every option you mention (and more) with the @Javascript with no success. Once the ckeditor.js is loaded I had many path errors (404) for js files (config.js, /lang/en.js, etc) and CSS files (editor.css, etc).

Any idea how con I solved it (again is not a war web environment but an OSGI jar bundle)?

Help will be appreciated.