Static Assets Cache with Spring - javascript

I am developing a web application and intend to make use of the performance boost that caching resources give, but it comes with an important caveat. Whenever I updated a static file, users wouldn't see these changes immediately, and so had to disable the browser's cache in order to fetch the newest version. In order to fix this issue, I decided to add static assets versioning. Which works as intended with the following code.
#Override
public void addResourceHandlers(final ResourceHandlerRegistry registry) {
registry.addResourceHandler("/**")
.addResourceLocations("classpath:/static/")
.setCacheControl(CacheControl.maxAge(365, TimeUnit.DAYS))
.resourceChain(true)
.addResolver(new VersionResourceResolver().addContentVersionStrategy("/**"))
// Costume made transformer to handle JS imports
.addTransformer(new JsLinkResourceTransformer())
.addTransformer(new CssLinkResourceTransformer());
}
#Bean
public ResourceUrlEncodingFilter resourceUrlEncodingFilter() {
return new ResourceUrlEncodingFilter();
}
Everything was working as intended, except for one simple detail. JS imports were still loading the none versioned files. So something like import * from './myscrypt.js', would not work properly.
I had to implement my own resource transformer in order to avoid that new caveat. The implementation does it's job, and now my imports would fetch the right version, like import * from './myscript-149shdhgshs.js'. Then, I thought everything was fixed, but a new issue came up. Here is the scenario, which will make it easier to understand.
I load a page that includes script.js
Then Spring serve me with the correct version of the file script-v1.js
After that, script-v1.js imports functions from myscript.js
The browser fetch the right version of the script myscript-v1.js
The two of them get cached locally
I update myscript.js making a new version myscript-v2.js
I reload the page, but since script-v1.js was stored in cache, I load it with the old import myscript-v1.js, even though there is a new version
I just can't seem to make it work. Of course, I could simply stop using js modules and instead just load all the scripts at once, but that is not the solution I want to go for. Would there be a solution for js module versioning using Spring?

My way of solving this cached version will be using app version. If the project is built on Maven, I see you're using classpath resource for static file resolutions. Whenever there is a new change to js file, you will have new build and if you could change the version on every build, here is my workaround would look like.
pom.xml
<version>0.1.0</version>
<build>
<resources>
<resource>
<directory>src/main/resources</directory>
<filtering>true</filtering>
</resource>
</resources>
application.yml
build:
version: #project.version#
This will push version from pom.xml to application.yml both dev on IDE and built jar
Controller
I'm using mustache view resolver here.
#Controller
public class HelloController {
#Value("${build.version}")
private String version;
private String encodedVersion;
#PostConstruct
public void setup() {
encodedVersion = new String(Base64.getEncoder().encode(version.getBytes())).replace("=", "");
}
#RequestMapping("/home")
public ModelAndView home() {
ModelAndView mv = new ModelAndView();
mv.setViewName("home.html");
return mv;
}
#ModelAttribute("version")
public String getVersion() {
return encodedVersion;
}
}
home.html
<html>
<head>
<script type="text/javascript" src="/pop.js?cache={{version}}"></script>
<script type="text/javascript">
window.version = "{{version}}" // in case you need this somewhere
</script>
</head>
<body>
<h1>Home1</h1>
version: {{version}}
</body>
</html>
Manipulating existing js files
#Configuration
#AutoConfigureAfter(DispatcherServletAutoConfiguration.class)
public class Config implements WebMvcConfigurer {
#Value("${build.version}")
private String version;
private String encodedVersion;
#PostConstruct
public void setup() {
encodedVersion = new String(Base64.getEncoder().encode(version.getBytes())).replace("=", "");
}
#Override
public void addResourceHandlers(ResourceHandlerRegistry registry) {
registry.addResourceHandler("/**").addResourceLocations("classpath:/static/").setCacheControl(CacheControl.maxAge(365, TimeUnit.DAYS)).resourceChain(true)
.addResolver(new VersionResourceResolver().addContentVersionStrategy("/**"))
.addTransformer(new ResourceTransformer() {
#Override
public Resource transform(HttpServletRequest request, Resource resource, ResourceTransformerChain transformerChain) throws IOException {
// Be aware of side effects changing line break
String result = new BufferedReader(new InputStreamReader(resource.getInputStream())).lines().collect(Collectors.joining("\n"));
result = result.replace("{{cacheVersion}}", encodedVersion);
return new TransformedResource(resource, result.getBytes());
}
});
}
}
pop.js
import mod1 from './mod1.js?cache={{cacheVersion}}';
function dis() {
console.log("hello")
}
Since the version is added as ModelAttribute it will be available in all request mapping. For every version, this will be changed and the way you pull files can be using this cache version variable.

Related

How can I add the external jar file into react native application and use as a plugin into the javascript file for both android and iOS

Actually, I have existing SDKs and I wanted to use that SDK in the react native app.
For android
I tried adding the jar file into the libs folder of /android/app/
Added dependencies into file /android/app/build.gradle
implementation fileTree(include: ['*.jar'], dir: 'libs')
But I did not get how can I use these jar files in my js file. How can I create the object and call the methods?
The main concern is how can I use the external java libraries in my react native app?
You should use native modules them are bridge between JS and native
https://reactnative.dev/docs/native-modules-android
Example
public class DummyModule extends ReactContextBaseJavaModule {
MyDummyClass dummy // this context
public DummyModule(final ReactApplicationContext reactContext){
super(reactContext);
}
#Override
// getName is required to define the name of the module represented in
// JavaScript
public String getName() {
return "DummyModule";
}
#ReactMethod
public void startMyClass() {
this.dummy = new MyDummyClass();
}
#ReactMethod
public void fooActionClass() {
if(this.dummy != null){
this.dummy.fooAction();
}
}
}
In your javascript code
import { NativeModules } from 'react-native';
const dummyModule = NativeModules.DummyModule;
dummyModule.startMyClass();
// Make sure that u call the action when the class is instanciated.
dummyModule.fooActionClass();
Usefull question as well Sending hashmao from java to react native

Spigot Plugin from tutorial failing to run

I'm trying to run a simple minecraft plugin I've built through a local spigot server. the plugins is
Main.java
import org.bukkit.plugin.java.JavaPlugin;
public class Main extends JavaPlugin {
#Override
public void onEnable() {
getLogger().info("onEnable has been invoked!");
// TODO Insert logic to be performed when the plugin is enabled
}
#Override
public void onDisable() {
getLogger().info("onDisable has been invoked!");
// TODO Insert logic to be performed when the plugin is disabled
}
}
plugin.yml
name: Kolo
version: 1.0
author: will
commands:
Hello:
Everytime I export the project as a jar to the plugins folder of my server and run my server it gives me this
[20:31:29 INFO]: Set PluginClassLoader as parallel capable
[20:31:29 ERROR]: Could not load 'plugins/Kolo.jar' in folder 'plugins'
org.bukkit.plugin.InvalidPluginException: Cannot find main class `me.will.Kolo'
at org.bukkit.plugin.java.PluginClassLoader.<init>(PluginClassLoader.java:66) ~[minecraft_server.jar:git-Spigot-db6de12-18fbb24]
Can someone explain this to me? Idk if the info part of the code means anything but I searched it up and couldn't really decipher any instructions on how to go about doing what it says. Im still learning java and I feel like its mostly me forgetting a simple thing but I wanted to check on here to see if maybe one of you could clarify for me so I'll understand better.
You're missing the "main" attribute in your plugin.yml file.
In case your folder structure is like "me/will/Kolo/Main.java", it should be like this:
plugin.yml
name: Kolo
version: 1.0
author: will
main: me.will.Kolo.Main
commands:
Hello:

'Error: `VideoPlayer` has no propType for native prop `VideoPlayerView.scaleY` of native type `number`

VideoPlayerView is a custom native component made in Android and IOS for my particular project.
Here is a part of code exporting native module named VideoPlayerView made by ReactVideoManager.java
public class ReactVideoManager extends SimpleViewManager<MeasureChangedVideoView> {
public static final String REACT_CLASS = "VideoPlayerView";
private ThemedReactContext mReactContext;
#Override
public String getName() {
return REACT_CLASS;
}
#Override
protected MeasureChangedVideoView createViewInstance(ThemedReactContext reactContext) {
mReactContext = reactContext;
return new MeasureChangedVideoView(reactContext);
}
#ReactProp(name = "url")
public void setUrl(MeasureChangedVideoView view, #Nullable String url) {
Uri uri = Uri.parse(url);
view.setVideoURI(uri);
view.setMediaController(new MediaController(mReactContext));
view.setOnPreparedListener(new MediaPlayer.OnPreparedListener() {
#Override
public void onPrepared(MediaPlayer mp) {
mp.start();
}
});
}
}
Below is the error log found for the module:
Error: `VideoPlayer` has no propType for native prop `VideoPlayerView.scaleY` of native type `number`
The corresponding js component for this native view is VideoPlayer.js:
var React = require('react-native');
var { requireNativeComponent } = React;
class VideoPlayer extends React.Component {
render() {
return <VideoPlayerView {...this.props} />;
}
}
VideoPlayer.propTypes = {
url: React.PropTypes.string
};
var VideoPlayerView = requireNativeComponent('VideoPlayerView', VideoPlayer);
module.exports = VideoPlayer;
The very same module used to work in a different project B. The only difference found was presence of a react.gradle file in project B.
There is absolutely no clue where react.gradle file is generated. Also I tried restarting packager, cleaning the project building again.
The gradle dependency used in project is
compile "com.facebook.react:react-native:0.14.+"
One should definitely use the latest version and the way specified in docs https://facebook.github.io/react-native/docs/native-components-android.html#content should work fine.
So It turns out that I was using React version 0.13+ (in package.json) and the way of adding properties using #Reactprop annotation was introduced in gradle 0.14 dependency and this way doesn't quite work well with the older version of React-native(0.13).
If due to some reason you happen to work on 0.13 or pre, the way of mixing in of props manually as specified in https://github.com/facebook/react-native/issues/3478 must be followed.

how to add/access a JS compiled from GWT to/in another external html/JS project

I am tasked to compile the gwt project(which doesnot include HTML CSS) into JS files and add the same to an external JS/HTML file( which is in different project).
here is the java code which has to be compiled:
1. Client class:
package com.dell.supportassist.gwt.collectionReport.client;
import org.timepedia.exporter.client.Export;
import org.timepedia.exporter.client.Exportable;
#Export("HelloWorld")
public class HelloWorld implements Exportable {
public String sayHello(){
return "Hello";
}
}
EntryPoint Class:
package com.dell.supportassist.gwt.collectionReport.client;
import org.timepedia.exporter.client.Export;
import org.timepedia.exporter.client.Exportable;
#Export("HelloWorld")
public class HelloWorld implements Exportable {
public String sayHello(){
return "Hello";
}
}
My issue is, once the above gwt project/classes are complied, i want to access 'sayHello()' method in ma external javascript like this:
var person = hello.sayHello();
system.log(person);
But this is throwing a run time error saying 'hello' is not defined.
P.S I am trying to use the GWT compiled JS in an external HTML, JS Present in Durandaljs Framework.
Haven't done this kind of stuff for quite some time but you could achieve this using JSNI, which allows you to write native JS in GWT. From JSNI you can reference your GWT methods. That way you could define a function (on window, most typically) which would then become available to regular JS. From this JSNI-method you can reference your GWT/Java code.
Code example:
public class MyModule implements EntryPoint {
static {
export();
}
/**
* Makes our setData method accessible from plain JS
*/
private static native void export() /*-{
$wnd.setData = #my.package.MyModule::setData(Lcom/google/gwt/core/client/JavaScriptObject;);
}-*/;
private static void setData(JavaScriptObject javaScriptObject) {
// this method is now reachable as window.setData
}
}

How to expose class functionality in GWT

I have a class library written in Java and want to convert it to Javascript. All methods are pretty simple and mostly have to do with manipulating collections. I have this one class, GameControl, which I could instantiate and I want its methods exposed to other Javascript code on the page.
I thought to use GWT. I have a running project in GWT which compiles, but I can't figure out how to expose my instance (+functionality) of the GameControl class.
I thought using JSNI to expose my object should work, but it didn't. This is the short version of how it look like right now:
GameEntryPoint.java
import com.google.gwt.core.client.EntryPoint;
public class GameEntryPoint implements EntryPoint {
private GameControl _gameControl;
#Override
public void onModuleLoad() {
_gameControl = new GameControl();
expose();
}
public native void expose()/*-{
$wnd.game = this.#game.client.GameEntryPoint::_gameControl;
}-*/;
}
GameControl.java
package game.client;
public class GameControl {
public boolean isEmpty(int id){
// does stuff...
return true;
}
}
So, GWT indeed compiles the code, and I see that there is a GameControl_0 object being built and set into $wnd.game, but no isEmpty() method to be found.
My expected end result is to have a window.game as an instance of GameControl with all public methods GameControl exposes.
How can I do this?
Edit
As per #jusio's reply, using JSNI to expose window properties explicitly worked, but it was too verbose. I'm trying the gwt-exporter solution. Now I have
GameEntryPoint.java
package game.client;
import org.timepedia.exporter.client.ExporterUtil;
import com.google.gwt.core.client.EntryPoint;
public class GameEntryPoint implements EntryPoint {
#Override
public void onModuleLoad() {
ExporterUtil.exportAll();
}
}
RoadServer.java
package game.client;
import org.timepedia.exporter.client.Export;
import org.timepedia.exporter.client.ExportPackage;
import org.timepedia.exporter.client.Exportable;
#ExportPackage("game")
#Export("RoadServer")
public class RoadServer implements Exportable {
int _index;
int _id;
public RoadServer(int index,int id){
this._id=id;
this._index=index;
}
}
but still none of the code is exported (specifically not RoadServer).
You have exposed only instance of the GameControl. If you want to expose other methods, you'll have to expose them as well.
For example:
public native void expose()/*-{
var control = this.#game.client.GameEntryPoint::_gameControl;
var gameInstance = {
gameControl: control,
isEmpty:function(param){
control.#game.client.GameEntryPoint::isEmpty(*)(param);
}
}
$wnd.game = gameInstance;
}-*/;
Also there is a framework called gwt-exporter, it might make things easier for you
This may help.
http://code.google.com/p/gwtchismes/wiki/Tutorial_ExportingGwtLibrariesToJavascript_en

Categories