Giter Site home page Giter Site logo

cuba-platform / documentation Goto Github PK

View Code? Open in Web Editor NEW
25.0 6.0 45.0 63.76 MB

CUBA Platform Documentation

Home Page: https://www.cuba-platform.com

License: Creative Commons Attribution 4.0 International

Groovy 8.01% Java 54.58% JavaScript 3.82% Shell 0.37% CSS 8.72% HTML 13.74% Batchfile 0.11% Ruby 0.31% Makefile 0.06% SCSS 10.28%
cuba-platform documentation manual

documentation's Issues

Thread safety of Configuration Interfaces

Configuration interfaces may be used as middleware beans' attributes, for example:

class MyBean {

    protected  MyConfiguration myConfig;

    @PostConstruct
    public void init() {
        myConfig = configuration.getConfig(MyConfiguration.class);
    }

    public void doSomething() {
        if (myConfig.isSomePropertyEnabled()) {
            ....
        }
    }
}

Thus,

  • ConfigPersisterImpl is thead-safe,
  • Server implementation does not cache values, so it always read the newest values from ConfigStorage.

Production settings

Relates to
See also

Document the recommended production settings:

  • Tomcat (local.app.properties etc),
  • production mode ON for vaadin in web.xml
  • production mode ON for REST API
  • unset auto-filled default login and password
  • (optional) if app is running under nginx, the correct version of proxy http and certain headers should be set, otherwise, websockets will not start up, and vaadin will fall into long polling (this can be detected in JS console)

The real question from a client: We want to deploy the portal app on a separate tomcat instance on a different physical server. What config needs to be set for it to know the location of the core app?

How to replace Service implementation completely

Without inheritance from base service from app component

At the moment, we have to annotate new service implementation with @service annotation and exclude this class from component-scan:

<context:component-scan base-package="com.haulmont.cuba">
    <context:exclude-filter type="regex" expression="com\.haulmont\.cuba\.web\.sys\.CustomMyServiceBean"/>
</context:component-scan>

How to customize validation notification in Window

Relates to
At the moment, it is implemented in com.haulmont.cuba.web.gui.WebWindow#showValidationErrors and cannot be changed.

See: https://www.cuba-platform.com/support/topic/how-to-change-the-position-of-error-alert

New configuration property: cuba.gui.validationNotificationType
Default value: TRAY

Valid value: one of com.haulmont.cuba.gui.components.Frame.NotificationType (TRAY, TRAY_HTML, HUMANIZED, HUMANIZED_HTML, WARNING, WARNING_HTML, ERROR, ERROR_HTML)

You can override default behaviour for concrete Window/Frame in showValidationErrors() method:

    @Override
    public void showValidationErrors(ValidationErrors errors) {
        super.showValidationErrors(errors);
    }

Time Frame description for cron scheduling type

Time frame – if Start date is specified, Time frame defines the time window in seconds, during which the task will be launched after startDate + period * N time expires. If Time frame is not specified explicitly, it is equal to period / 2.
If Start date is not specified, Time frame is ignored, i.e. the task will be launched at any time after Period since the previous execution of the task expires.

Describe also cron scheduling with time frame

Fix integration test examples

https://doc.cuba-platform.com/manual-6.6/integration_tests_mw.html

  1. Get rid of:
  • new Customer() - creation of entities with new is dangerous and we show warning inspection in Idea
  • new ArrayList<>(Arrays.asList( - verbose, use simple Arrays.asList(
  1. There is no example of test-app.properties file, usually it contains important options:
cuba.confDir=${user.dir}/test-home/cuba/conf
cuba.logDir=${user.dir}/test-home/cuba/logs
cuba.tempDir=${user.dir}/test-home/cuba/temp
cuba.dataDir=${user.dir}/test-home/cuba/work
  1. Specify full package name for sales-app.properties, test-app.properties, since new projects always have app.properties files inside of root package

JS Slider does not set values to state on `valueChanged` from Client

https://doc.cuba-platform.com/manual-6.6/js_library_sample.html

SliderServerComponent should have the following constructor:

    public SliderServerComponent() {
        addFunction("valueChanged", arguments -> {
            JsonArray array = arguments.getArray(0);
            double[] values = new double[2];
            values[0] = array.getNumber(0);
            values[1] = array.getNumber(1);

            getState(false).values = values;

            listener.valueChanged(values);
        });
    }

Also we have to override the following method:

    @Override
    protected SliderState getState(boolean markAsDirty) {
        return (SliderState) super.getState(markAsDirty);
    }

Entity states and em.merge

All newbie developers and many experienced ones don't understand the meaning of managed entity state and purpose of em.merge. For them em.merge call is "save entity changes to DB", like em.save().
Existing documentation pages (1, 2) are not enough. Too brief, people just don't get the sense without real code.

TODO: add examples, recommended scenarios, explanations of side effects.

What can be added:

  • explanation of what exactly em.merge is doing under the hood. Side effects: optimistic lock checking. When we need to use em.merge and when we don't need it. Code example of justified em.merge usage.
  • code example of em.reload usage.
  • code example for this sentence:

Any changes of the instance in Managed state will be saved to the database when the transaction that the EntityManager belongs to is committed.

Hot deploy settings

Document how HD works:

  • Help -> Hot Deploy settings in Studio
  • Help -> Instant Hot deploy setting in Studio
  • Run ->Hot deploy conf

If Instant Hot deploy is deactivated, one can run Run ->Hot deploy conf:

If Instant Hot deploy is active:

  • Help->Instant Hot deploy settings are applied
  • files in global module are not hot deployed at all (waiting for the fix)
  • changes in services are hot deployed only from implementations, interface changes (in global) cannot be hot deployed
  • Groovy beans cannot be hot deployed (ticket)

Help -> Settings, ->Context help

The 'Instant hot deploy' checkbox allows you to turn off hot deploy.
If hot deployment is enabled, Studio dynamically updates the UI of your deployed web application as you apply or save changes to views, screens, messages or the main menu. На самом деле Instant hot deploy работает не только для UI (see above)

Run ->Hot deploy conf

Hot deploy conf runs the deployConf Gradle task which by default copies all sources of the web module into the application server's configuration directory.
The task can be configured in build.gradle if necessary. This command is usually excessive because Studio can instantly deploy changed source files of the web module if the Instant hot deploy for Web Client checkbox in the Screens section is set.

In fact: not only Web module and not all sources.

Instant hot deploy for Web Client checkbox in the Screens section - should be clarified.

UPDATE:

  1. What works:
    XML descriptors and screen controllers (including static methods), located in web and gui modules
    Middleware service implementations, located in core module

  2. What doesn't work:
    Any classes of the global module, including middleware service interfaces

  3. Other UI and middleware classes and beans, including their static methods, hot deployed ONLY IF some class from item 1 has also been changed. The reason for this is that class reloading is started by some signal: for screen controllers it is the screen reopening, for services - Studio generates special trigger file that is recognized by server and used to reload particular service class and all its dependencies.

See also https://www.cuba-platform.com/support/topic/hot-deploy-issue#comment-6654

Multi-threading considerations for BackgroundTask#run() implementation

Add examples of safe multi-threading.

Problem:
The run() method runs in a separate thread, not in the UI thread. It must be considered in case:

  • you need to pass parameters to the task,
  • call UI components from the task,
  • use changeable variables, also used from UI thread, from the task.

Main rules:

  • Changing UI components and datasources state from run() is a bad practice that can result in mystic Vaadin and Swing errors.
  • Read and update operations for any changeable state from run() (including datasources, components, and added fields of screen controllers) must be synchronized,
  • The best practice for background tasks would be to avoid synchronization at all with the help of Platform means.

These means are:

The task is coded in functional style divided into 3 parts:

  1. Prepare task parameters - in UI thread before running the task,
  2. run() performing a continuous action in a separate thread. It uses only prepared parameters and prepares some result which serves as a class-task parameter. Multiple results can be wrapped in POJO.
  3. The task result is applied to the interface and saved in some cache-fields, performed in done(), handleException(), handleTimeout().

Passing parameters via the task constructor seems to be more type-safe than by getParams().

Example №2:

// Here:
// parameter - String searchString
// result - List<AddressSuggestion>
// state - addresses and components

@Named("searchString")
protected TextField searchStringField;
@Named("addressDs")
private CustomDatasource<GuiSuggestion> addressDs;
@Named("searchProgressBar")
private Label searchProgressBar;

protected List<AddressSuggestion> addresses;
protected BackgroundTaskWrapper<Void, List<AddressSuggestion>> backgroundTaskWrapper = new BackgroundTaskWrapper<>();

protected class AddressSearchTask extends BackgroundTask<Void, List<AddressSuggestion>> {
    private String searchString;

    public AddressSearchTask(String searchString) {
        super(30);
        this.searchString = searchString;
    }

    @Override
    public List<AddressSuggestion> run(TaskLifeCycle<Void> taskLifeCycle) {
        List<AddressSuggestion> matches = addressSearchService.search(searchString);
        return matches;
    }

    @Override
    public void canceled() {
        addresses = Collections.emptyList();
        addressDs.refresh();
        setInSearch(false);
    }

    @Override
    public void done(List<AddressSuggestion> result) {
        addresses = result;
        addressDs.refresh();
        setInSearch(false);
    }
}

protected void setInSearch(boolean inSearch) {
    searchProgressBar.setVisible(inSearch);
}

...
// call
String searchString = searchStringField.getValue();
setInSearch(true);
backgroundTaskWrapper.restart(new AddressSearchTask(searchString));

Example №2

// Пример №2
// Расчёт цены заказа в фоновом потоке
// для сложного объекта-заказа, который редактируется на форме

// Т.к. во время выполнения задачи заказ может изменяться через компоненты редактирования
//  - то нельзя передавать в задачу объект, хранящийся в датасорсе.
//  Перед запуском задачи нужно создать объект-копию заказа, для которого и будет идти расчет

    @Inject
    protected Datasource<Order> orderDs;
    @Inject
    protected ProgressBar progressBar;
    @Inject
    protected Label priceLabel;

    private class PriceCalcTask extends BackgroundTask<Void, Price> {
        private Order orderCopy;

        public PriceCalcTask(Order orderCopy) {
            super(60, frame);
            this.orderCopy = orderCopy;
        }

        @Override
        public Price run(TaskLifeCycle<Void> taskLifeCycle) {
            return priceService.calculatePrice(orderCopy);
        }

        @Override
        public void done(Price result) {
            assignPriceToOrder(orderCopy, result);
            priceLabel.setValue(result.getTotal());
            progressBar.setVisible(false);
        }

        @Override
        public boolean handleException(Exception ex) {
            showNotification("Failed to calculate price", NotificationType.WARNING);
            progressBar.setVisible(false);
            return true;
        }
    }

// вызов
progressBar.setVisible(true);
Order orderClone = deepClone(orderDs.getItem());
priceTaskWrapper.restart(new PriceCalcTask(orderClone));

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.