spring-cloud-function-deployer deploy/undeploy function programmatically at runtime - spring-cloud-function

The spring-cloud-function-deployer examples all show the deployed function being loaded on startup i.e. the ApplicationContext is started with the necessary properties, pointing at the packaged jar to load.
Is there a way to call the deployer programatically at runtime, instead of relying on the auto-configuration? In case I want to deploy the function sometime after the application context has started, or if I want to deploy multiple functions from the same jar etc.
Also is there a way to undeploy any loaded functions, or is this simple as removing the function from the catalog?

as stated in the GH response, you absolutely can deploy functions at runtime.
String[] args = new String[] {
"--spring.cloud.function.location=target/it/bootapp/target/bootapp-1.0.0.RELEASE-exec.jar",
"--spring.cloud.function.definition=uppercase" };
ApplicationContext context = SpringApplication.run(DeployerApplication.class, args);
FunctionCatalog catalog = context.getBean(FunctionCatalog.class);
Function<String, String> function = catalog.lookup("uppercase");
// use the function
You can see sample deployments here and the corresponding test.

Related

AWS Lambda NodeJS locale/variable isolation

There is a concern about potential problem with reusable variables in aws-lambda.
A user's locale is passed as
Browser cookies => AWS API Gateway => Lambda (NodeJS 6.10)
On the server side localization is implemented with a static variable in a class. Presenting typescript code for clarity but can be done in pure ECMAScript.
Module Language.ts
export default class Language
{
public static Current: LanguageCode = LanguageCode.es;
}
Static Language.Current variable is used across different parts of the application for manual localization and it works perfectly on the client side (react + redux).
Lambda function
import {APIGatewayEvent, Context, Callback} from 'aws-lambda';
import Language from './pathToModule/Language.ts';
export const api = function(event: APIGatewayEvent, context: Context, callback: Callback)
{
Language.Current = event.headers.cookie.locale;
// do the logic here
}
Potential problem
According to AWS documentation NodeJS instances can be reused for different requests. It means that famous concurrent problems have to be considered, e.g.
User 1 calls lambda function. The locale is set to English.
In parallel user 2 calls the same lambda instance. The local is changed to Spanish.
User 1 code continues and reads modified (wrong) locale variable from the shared module Language.
How do you resolve this problem?
For convenience it is good to have only one place for locale change. As I understand the same concern exists for all famous i18n npm packages (i18next, i18n, yahoo i18n, etc).
One of the best practices for Lambda functions is to try and not write code which maintains state.
Here you are initializing the locale based on an initial request and applying it to all future requests, which is inherently flawed even on server based code, forget server less.
To fix this, you will need to initialize the localization library for each request, or at least maintain an in memory lazy map, which you can make use of use the current request's locale to achieve the desired localization.
There are several solutions:
Node JS container is reused only after a function process is finished (callback or error is occurred) (thanks to #idbehold). Thus there is always a unique context per a function call.
Refactor code and pass a locale variable back and force (#Yeshodhan Kulkarni suggestion).
For example, return a function as an intermediate result and use it before calling the result back.
var localizableResult = ...;
var result = localizableResult.Localize(requestedLocale).
If there is a need to use a local stack (kind of a thread context) for other projects there is a npm package node-continuation-local-storage.
Case 1 makes it really simple to use global variables for current locale.

Inject Jar and replace classes in running JVM

I want to be able to replace and add some classes to an already running JVM. I read that I need to use CreateRemoteThread, but I don't completely get it. I read this post on how to do it (Software RnD), but I can't figure out what it does and why. Besides that, it only introduces new classes, but doesn't change existing ones. How can I do it with C++?
You don't even need CreateRemoteThread - there is an official way to connect to remote JVM and replace loaded classes by using Attach API.
You need a Java Agent that calls Instrumentation.redefineClasses.
public static void agentmain(String args, Instrumentation instr) throws Exception {
Class oldClass = Class.forName("org.pkg.MyClass");
Path newFile = Paths.get("/path/to/MyClass.class");
byte[] newData = Files.readAllBytes(newFile);
instr.redefineClasses(new ClassDefinition(oldClass, newData));
}
You'll have to add MANIFEST.MF with Agent-Class attribute and pack the agent into a jar file.
Then use Dynamic Attach to inject the agent jar into the running VM (with process ID = pid).
import com.sun.tools.attach.VirtualMachine;
...
VirtualMachine vm = VirtualMachine.attach(pid);
try {
vm.loadAgent(agentJarPath, options);
} finally {
vm.detach();
}
A bit more details in the article.
If you insist on using C/C++ instead of Java API, you may look at my jattach utility.

Using a HTTP Module on a Virtual Directory in IIS

I have a default website in my IIS where I have created one virtual directory "wsdls".
I would want to gather statistics on how many requests are triggered to my virtual directory. This would need a request interception at web server level and gather statistics. "HTTPModule" was one of the many solutions I have considered which is suitable for such scenario. Hence I have started building one.
For testing purpose, I wanted to create a HTTP Module and apply it on a particular extension files (say *.wsdl) and on every GET request of any .wsdl files in this virtual directory, I will want to redirect the application to "www.google.com". This would demonstrate a good example of how HTTP Module can be used and deployed on IIS.
HTTPModule which is written using Visual Studio is shown below,
namespace Handler.App_Code
{
public class HelloWorldModule : IHttpModule
{
public HelloWorldModule(){
}
public String ModuleName{
get { return "HelloWorldModule"; }
}
// In the Init function, register for HttpApplication
// events by adding your handlers.
public void Init(HttpApplication application){
application.BeginRequest +=
(new EventHandler(this.Application_BeginRequest));
application.EndRequest +=
(new EventHandler(this.Application_EndRequest));
}
private void Application_BeginRequest(Object source,
EventArgs e)
{
// Create HttpApplication and HttpContext objects to access
// request and response properties.
HttpApplication application = (HttpApplication)source;
HttpContext context = application.Context;
context.Response.Redirect("www.google.com");
}
private void Application_EndRequest(Object source, EventArgs e)
{
//Nothing to be done here
}
public void Dispose() { }
}
}
Now I have done a build of this project for x64 version and I am able to browser successfully the "dll" file. Now I have to register this dll in IIS and whenever I try to access the *.wsdl files, the requests automatically divert to "www.google.com". Here is the next step I have done,
Then I have enabled the Handler mappings as shown below,
I am assuming that is it!! Nothing more to be done. I should be able to intercept the requests for all HTTP requests which are of the form "*.wsdl". This means whenever I access any wsdl from the server, control should be going back to google(Because of the logic written in begin request ). But unfortunately, I failed in achieving it. What can be done here?
One thing I noticed is that when you are trying to redirect to an external URL use
http://
So change
context.Response.Redirect("www.google.com");
to
context.Response.Redirect("http://www.google.com", true);
I could solve the problem what I am facing and below are the observations which were missing in my understanding and which helped me in solving my problem:
Locating proper web.config file :
Every website in IIS will be having a web.config file to have control over the application.
Since I am working with "Default Website", this refers to the directory "C:\\inetpub\\wwwroot"
There will be a "web.config" file which would be present in this director. Please create it if not already present.
Modifying web.config :
Once you have identified the file which needs to be modified, just add necessary module configuration to web.config
In this case, we would want to add a Module to the default website, the probably setting would be shown below,
Adding contents to bin directory :
Now if you try to run the application, the IIS would not find any dll or executable to run and hence we would need to keep the executables at a particular location.
Create a director if not already present with the name "bin" at the root of the directory and place all the dlls which you would want this website to execute. Sample shown below,
General Points to be considered:
Proper access must be given for the folder which consists of dll.
It is ideally not suggested to modify the entire website. It would be ideal if one works only on their web application.
If web.config is not found, we can create one.
If bin is not present in the web root directory, we can create one.

Persisting data in an axis object

Forgive me if this is a dumb question, I unfortunately have an assignment due! I am running apache axis under tomcat and need to deploy a simple web service class, see below.
I installed the counter file below as "MyCounter.jws" in the /tomcat/webapps/axis/ folder. Tomcat finds it and makes a corresponding MyCounter.xml. I use WSDL2Java on the XML file and client calls seem to work, but internal state is not saved:
Every time I call MyCounter.call from the client side, the return value is always 1. It seems the constructor is always called before the method call. How can I make it so the mycounter integer persists across requests?
public class MyCounter
{
int mycounter;
public MyCounter()
{
mycounter = 0;
}
public int call()
{
mycounter++;
return mycounter;
}
}
I think persisting is maybe the wrong word, I think what you mean is that the Java Class is not instantiated every time you call the service.
See: https://axis.apache.org/axis/java/user-guide.html#Scoped_Services
So you would need to change the Service definition yo achieve this.
I don't think that with the JWS files you will be able to configure the session scope. As the docs say:
https://axis.apache.org/axis/java/user-guide.html#JWS_Java_Web_Service_Files_-_Instant_Deployment
Quote:
Important: JWS web services are intended for simple web services. You
cannot use packages in the pages, and as the code is compiled at run
time you can not find out about errors until after deployment.
Production quality web services should use Java classes with custom
deployment.
So if you want to use such features you should consider using some of the other ways Axis offers to setup a WebService.
Also I would strongly recommend using Axis2 instead of Axis1:
http://axis.apache.org/axis2/java/core/
Axis1 can be quite complicated with the WSDD files to setup. Apart from Axis1 no more actively developed/maintained.

Java code to get currently running beanstalk version label?

From within a running Java application running on beanstalk, how can I get the beanstalk version label that is currently running?
[Multiple Edits later...]
After a few back-and-forth comments with Sony (see below), I wrote the following code which works for me now. If you put meaningful comments in your version label when you deploy, then this will tell you what you're running. We have a continuous build environment, so we can get our build environment to supply a label that leads to the check-in comments for the related code. Put this all together, and your server can tell you exactly what code its running relative to your source code check-ins. Really useful for us. OK now I'm actually answering my own question here, but with invaluable help from Sony. Seems a shame you can't remove the hard-coded values and query for those at runtime.
String getMyVersionLabel() throws IOException {
Region region = Region.getRegion(Regions.fromName("us-west-2")); // Need to hard-code this
AWSCredentialsProvider credentialsProvider = new ClasspathPropertiesFileCredentialsProvider();
AWSElasticBeanstalkClient beanstalk = region.createClient(AWSElasticBeanstalkClient.class, credentialsProvider, null);
String environmentName = System.getProperty("PARAM2", "DefaultEnvironmentName"); // Need to hard-code this too
DescribeEnvironmentsResult environments = beanstalk.describeEnvironments();
for (EnvironmentDescription ed : environments.getEnvironments()) {
if (ed.getEnvironmentName().equals( environmentName)) {
return "Running version " + ed.getVersionLabel() + " created on " + ed.getDateCreated());
break;
}
}
return null;
}
You can use AWS Java SDK and call this directly.
See the details of describeApplicationVersions API for how to get all the versions in an application.Ensure to give your regions as well (otherwise you will get the versions from the default AWS region).
Now, if you need to know the version deployed currently, you need to call additionally the DescribeEnvironmentsRequest. This has the versionLabel, which tells you the the version currently deployed.
Here again, if you need to know the environment name in the code, you need to pass it as a param to the beanstalk configuration in the aws console, and access as a PARAM.