HOAB

History of a bug

SpringBoot OSGI and templated OSGI modules with external constrant Jetty

Rédigé par gorki Aucun commentaire

Problem :

A very specific and particular problem.

I want to deploy  a SpringBoot application, in an OSGI environment, in a external Jetty server…

So I found solution do deploy Springboot in OSGI : here or here. Very good by the wat.

And also to deploy Springboot as war for an external Tomcat…

Too easy, my external Jetty server was rejecting Servlet 3.0 application startup… (hard coding for : 

context.setAttribute("org.eclipse.jetty.containerInitializers", initializers);

Yes, it's better, starting a 1,25 days of try and success !

Solution :

My first web application was deployed with Spring Dispatcher Servlet with a BundleActivator

My second web application was deployed with Spring DispatcherServlet AND a SpringBootApplication intialized with the ressource loader of the BundleActivator (as in the OSGI example) : component-scan is working ! 

My third web application was switching to an initialization Servlet 3.0 with an extension of ServletContainerInitializer, thanks to harded jetty configuration, it's not working

My third and a half remove all web.xml content to keep only a context listener as my Jetty was doing nothing : 

<?xml version="1.0" encoding="ISO-8859-1"?>
<web-app xmlns="http://java.sun.com/xml/ns/javaee"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd"
         version="3.0">

    <!-- ServletContainerInitializer can not be used here -->
    <!-- XXXXXXX is putting in hard : context.setAttribute("org.eclipse.jetty.containerInitializers with only Jasper -->

    <!-- At the end, it's possible to load SpringBoot with this context listener -->
    <listener>
        <listener-class>
            com.hexagon.introscope.extension.MyContextLoaderListener
        </listener-class>
    </listener>

</web-app>

My fourth web application was trying to load the Springboot application file as a YAML file (why so simple !), resolved with a “hack” :

  • The ServletContext has a way to store the configuration directory, during the initialization, I saved it somewhere : HpaContextLoaderListener.getConfigDirectory() 

The listener loader : 

package com.xxxxx.extension;

import org.eclipse.jetty.webapp.WebAppContext;
import org.springframework.web.context.WebApplicationContext;

import javax.servlet.ServletContext;
import javax.servlet.ServletContextEvent;
import javax.servlet.ServletContextListener;
import javax.servlet.ServletException;
import java.io.File;
import java.nio.file.Path;

public class MyContextLoaderListener implements ServletContextListener {

    private static File configDirectory;

    public static File getConfigDirectory() {
        return configDirectory;
    }

    public void contextInitialized(ServletContextEvent event) {
        ServletContext servletContext = event.getServletContext();
        MySpringBootHexagonActivator app = new MySpringBootHexagonActivator();

        try {
            configDirectory = servletContext.getAttribute("introscope.em"));
            
            InstantiateHelper.getValueByMethod(servletContext, "setExtendedListenerTypes", new Class[]{boolean.class}, true);

            app.onStartup(event.getServletContext());
        } catch (ServletException e) {
            throw new RuntimeException(e);
        }
    }

    public void contextDestroyed(ServletContextEvent event) {
    }
}

 

@SpringBootApplication
public class MySpringBootHexagonActivator extends SpringBootServletInitializer {

    protected SpringApplicationBuilder createSpringApplicationBuilder() {
        SpringApplicationBuilder builder = super.createSpringApplicationBuilder();
        Map<String, Object> properties = new HashMap<>();
        Path propertyFile = Path.of(MyContextLoaderListener.getConfigDirectory().getPath(), "application-extension.yml");
        properties.put("spring.config.location", propertyFile.toString());
        builder.properties(properties);
        builder.resourceLoader(HpaHexagonActivator.getResourceResolver());

        return builder;
    }
}

The HpaHexagonActivator.getResourceResolver() is the trick from SpringBoot OSGI example to load classes from Bundle classpath. I store the ressources resolver in a static variable that I can access from everywhere.

A word on that one : 

InstantiateHelper.getValueByMethod(servletContext, "setExtendedListenerTypes", new Class[]{boolean.class}, true);

I'm in OSGI, Jetty controls the listener class, and Springboot is unknown. You can authorized additional listeners but in my case, Jetty is not exported by the other OSGI package, not able to call it simply so… reflection : InstantiateHelper is basically a helper class to call methods by reflection.

 

At the end : 

  1. Springboot starts
  2. Springboot scans package offered by BundleActivator ressource locator
  3. Springboot can use an external configuration file, in YAML
  4. Springboot starts REST & Database
  5. It was hard.

Of course, lot of tries between these big steps !

The goal is to extend a 3rd party product without having source code.

Angular @Input array was not updated.

Rédigé par gorki Aucun commentaire

Problem :

Here again, a common problem, the @Input attribute of the child element was not updated.

Good to know : 

  • ngOnChanges is called when the @Input attribute is already updated
  • common problem : the @Input parameter is an array and its reference is not modified, i.e, the array is the same even if it has different elements or new elements

No, in my case, the symptoms was : 

  1. ngOnChanges input is launch
  2. SimpleChanges event contains the data in “currentValue
  3. but array is displayed as [] even with values.

Yeah, tricky. I understand that array may be an object instead of a real array… but why. 

Solution :

Part of the problem was that the value sent to @Input was an array, but not initialized.

And a small function used to have unique value : 

  private pushIfNotExist<T>(array:T[], element:T) {
    if (array.indexOf(element) === -1) {
      array.push(element);
    }
  };

and I call it like that : 

pushIfNotExist(myArray, randomValue)

And with an empty javascript variable, well : no problem, it works, in a given way.

Adding : 

myArray = []

just before, everything works as a charm.

I lose time here, because I had a doubt on where was the issue : display on browser console, workflow for triggering ngOnChange, …

 

 

Lire la suite de Angular @Input array was not updated.

Adding a custom order to woocommerce do not update the post_status

Rédigé par gorki Aucun commentaire

Problem :

Adding a status to Woocommerce.

A lot of resources on internet, for example here

It was not working… I had the status in the list, but when saving the command, the old post status was kept…

1h search….

Solution :

My new order key, especially the post status / order status : wc-clickreserver-ready
To be used in : 

register_post_status( wc-clickreserver-ready, ...

Well, -ready is not a good idea

Perfectly working with :

register_post_status( wc-clickreserver, ...

Lire la suite de Adding a custom order to woocommerce do not update the post_status

Facebook and Woocommerce - resync

Rédigé par gorki Aucun commentaire

Problem :

Times to times, Facebook and Woocommerce lost their synchronization. 

In this case, the solution is to reset everything (if you have on Facebook, only your Woocommerce products !!)

Some information there :

But if your catalog is connected to a shop you can not delete your catalog…

And for any reason, the bulk requests was not working for me : accepted, no error, but still all the products ! 

I used : 

https://graph.facebook.com/v15.0/<my shop>/items_batch

{"allow_upsert": "true", "item_type": "PRODUCT_ITEM", "requests": [
{"method": "DELETE", "data": {"id": "1234"}},
{"method": "DELETE", "data": {"id": "2345"}}
{"method": "DELETE", "data": {"id": "3456"}}
]}

Solution :

So I tried to build the request by myself with a short python file : 


import requests

BASE_URL_FACEBOOK = 'https://graph.facebook.com/v16.0'
BASE_URL_FACEBOOK_BOUTIQUE = f'{BASE_URL_FACEBOOK}/<your shop>'
TOKEN = "yourToken"

# You must initialize logging, otherwise you'll not see debug output.
#logging.basicConfig()
#logging.getLogger().setLevel(logging.DEBUG)

url="/products?bulk_pagination=false&summary=true&limit=1000"
print(f'Calling GET : {url}')
hdr = {'User-Agent': r'Facebook-for-WooCommerce/2.6.27 (WooCommerce/6.8.2; WordPress/5.9.5)', 'Accept':'*/*', 'Content-Type': 'application/json', 'Authorization': f"Bearer {TOKEN}"}
response = requests.get(BASE_URL_FACEBOOK_BOUTIQUE + url, headers=hdr)
if response.status_code == 200 or response.status_code == 201:
    print(f'GET OK')
    products_response = response.json()
    delete_products_requests = list()
    print(products_response['summary']['total_count'])
    for product in products_response['data']:
        id = product['id']
        delete_command = {'method': 'DELETE', 'data': {"id":id}}
        delete_products_requests.append(delete_command)
        
    delete_request = dict()
    delete_request['allow_upsert'] = 'true'
    delete_request['item_type'] = 'PRODUCT_ITEM'
    delete_request['requests'] = delete_products_requests

    url="/items_batch"
    print(json.dumps(delete_request))
    response = requests.post(BASE_URL + url, headers=hdr, data=json.dumps(delete_request))
    print(response.status_code)
    print(response.text)
    response.raise_for_status()
    handles = response.json()['handles']
    print(f'handle = {handles}')

And follow the request with : 

https://graph.facebook.com/v16.0/<shop id>/check_batch_request_status?handle=<myHandle from previous request>

Still no error, but nothing deleted (we can follow the batch request in 

Facebook Business Manager > Catalog > Data Source > Application 

At the end, I found the delete request on a single product (simple by slowest) :

import requests


BASE_URL_FACEBOOK = 'https://graph.facebook.com/v16.0'
BASE_URL_FACEBOOK_BOUTIQUE = f'{BASE_URL_FACEBOOK}/<your shop>'
TOKEN = "yourToken"

url="/products?bulk_pagination=false&summary=true&limit=1000"
print(f'Calling GET : {url}')
hdr = {'User-Agent': r'Facebook-for-WooCommerce/2.6.27 (WooCommerce/6.8.2; WordPress/5.9.5)', 'Accept':'*/*', 'Content-Type': 'application/json', 'Authorization': f"Bearer {TOKEN}"}
response = requests.get(BASE_URL_FACEBOOK_BOUTIQUE + url, headers=hdr)
if response.status_code == 200 or response.status_code == 201:
    print(f'GET OK')
    products_response = response.json()
    products = dict()
    delete_products_requests = list()
    print(products_response['summary']['total_count'])
    for product in products_response['data']:
        id = product['id']
        url=f"/{id}"
        response = requests.delete(BASE_URL_FACEBOOK + url, headers=hdr)
        print(f'{response.status_code} : {response.text}')
        if (response.status_code != 200):
            print(f'Can not delete product {id}')

 

 

 

 

CA Apm Introscope and tracer not executed

Rédigé par gorki Aucun commentaire

Problem :

Silent code :) Configuration was OK, or seems to be, logs was OK but nothing happen.

For a project, I use Broadcom CA APM, formely Introscope, I created a custom Tracer, adding the required configuration but my tracer was not executed. Furthermore, another “standard” tracer was not executed also.

Solution :

Easy steps : 

  • Check the agent logs : ERROR is displayed :
    • [IntroscopeAgent.Agent] Unable to create tracer factories for the following class (library not found): 
  • Put agent logs in VERBOSE mode, I would had the solution directly
    • Same information class not found.
  • But because I everything for remote debug ready, I lost time but learn things
    • My tracer was using the same flag as the standard one : if one tracer can not be created, the whole flag is not enabled.
  • You can trace the classes to check if they are correctly instrumented compared to AutoProbe log. I never, never had a difference hier. If it says it's instrumented, then it is.
  •  

At the end the problem is "class not found" for one of the 2 tracers. So simple

And the class was of course present in the jar file. So it was related to the declaration of this class in the MANIFEST.MF required for CA APM Introscope extension : 

com-wily-Extension-Plugin-XXXX-Name: XXXX Frontend Tracer
com-wily-Extension-Plugin-XXXX-Type: tracer
com-wily-Extension-Plugin-XXXX-Version: 1
com-wily-Extension-Plugin-XXXX-Entry-Point-Class: com.xxx.xxxx.MyTracer

 

Lire la suite de CA Apm Introscope and tracer not executed

Fil RSS des articles