JDev 12.2.1.3: Creating a shared skin jar (Part 1)

In earlier versions of JDev, skins have been created either by pure code or by using the free Skin Editor. However, since JDev 12.2.1.x the skin editor has been integrated into JDeveloper itself.

A couple of questions in the ODC JDeveloper space are about how to create a skin with JDev which can be deployed as a shared library to a WebLogic Server. I gave this a try and it turned out, that you can build an ADF library jar from a skin project but you can’t use images to this jar which you might want to use in the application.

In this blog, I’ll show how to create a skin with resources like images and how to build a jar file from the skin together with the images and deploy it aa s shared jar to a WeblogicServer.

Building a skin project

The first part is to build a small skin project. The project we use to create a minimal skin, just to show that the skin is changing something. Then we add some images to the skin which we want to use in the application which uses the skin. Such images can e.g. used on an af:button component.

We start by creating a new application as an ‘ADF Fusion Web Application’

As we don’t need the created model project we delete it completely

If you get another dialog, telling you that you can’t undo the action, answer ‘Yes’ to delete the project. Now you should see a workspace with just the one project:

Know that we have a project we add a skin and e.g. add some skin selectors to change to the color of the button text. For this, we right click the ‘Web Content’ folder in the project and select ‘New from Garaly’ and then select ‘ADF Skin’ from the ‘JSF/Facelets’ node and fill in the basic information:

This will create the needed css file and the descriptors which define our skin (trinidad-config.xml and trinidad-skins.xml).

We open the sharedskin.css file if it’s not open already and switch to source mode. Here we add two simple skin selectors

which are changing the color of the text of a button and a link. You can add more sophisticated selectors but for this blog, it’s enough to show the working skin. To make it more interesting, and because that’s the real reason for this blog, we add some images to the skin which we like to use in the application using the skin. We add the images into a new folder like shown below

The reason for this structure is, that to read the images from the jar in the consuming application, we need a special resource loader. In case of ADF it’s the resource servlet which listens to the URL pattern ‘/adf/’’. This servlet is installed automatically for ADF Web Applications and is configured in the web.xml file

The final task for the skin project is to create a jar file which we can use in other applications. The easiest way to get such a jar is to create an ADF Library deployment descriptor. Open the project properties of the skin project and select the ‘Deployment’ node

And click the ‘New Profile’ icon, select to create an ADF Library Jar

and click ‘OK’. The remaining dialogs you can just click ‘OK’ or ‘Finish’.

To create the library we have to execute the descriptor by right-clicking on the project and selecting ‘Deploy’ and choosing the ‘sharedskinadflib’

This will create the jar in the ‘deploy’ folder of the project.

Sharing the skin with other applications

To share the skin with other application we can

  1. Create an ADF Library
    1. Add this library to the other application
    2. Add this library as a shared library to a WebLogic Server
  2. Create a normal jar
    1. Add this library to the other application
    2. Add this library as a shared library to a WebLogic Server

For this blog, we want to use option 2b. This allows to create the skin once, deploy it to a server and use it in every other application. In the next paragraph, we try out option 1a to show the problems when reading resources from a jar file.

Using a shared skin

Option 1a

We start with option 1a, just to show the problem when we try to read a resource from a jar. We build another ADF Fusion Web application and add the skin as ADF library from a ‘File System Connection’ which we create and let it point to the ‘deploy’ folder

Right-click on the ‘sharedskinadflib’ and add it to the new sample project. This will make the skin available to the application. To use the skin we have to add a skin to the application like we did to create the skin project. The difference is that we now choose the shared skin as the base skin

Creating a new page and adding a button and/or a link to the page we see the new style introduced by the ‘sharedskin’

So, the shared skin is working. Well, yes, but what about the images we added to the ‘sharedskin’?

Let’s try to add one to the button. In the property editor, we select the icon property of the button and click ‘Edit’ to get

However, we don’t see any image in the whole project. As we know where we put the images (or we can look into the sharedskinadflib) we can just add the path to the image like ‘skins/sharedskin/adf/images/home.png’ and we see the image

Running the application we get the page with the button but don’t see the image

Using DeveloperTools we see that the resource couldn’t be found. Inspecting the button element we see

The path to the image is not found 😦

If we change the address of the image to ‘/adf/images/home.png’ to use the resource servlet we still get an error

The reason is that the resource servlet expects the resources in a different path inside the jar. Every resource which should be read from a jar should be in a folder named ‘META-INF’.

The ADF library did not put the images into the META-INF folder

The problem is that we can’t change the layout of the ADF Library. When you create an ADF Library there is no option to make any changes to the content of the jar.

The conclusion is that using a skin in an ADF Library is problematic if there are other resources which you need to share.

To be continued…

In the final part 2 of the series, we see how the skin can be shared with other applications.

Advertisements

JDeveloper: access resources from the applications jar or classpath in the Model or ViewController

This bog describes how to read resources, which are part of the applications classpath and are deployed with the application.

Use Case
You want to read a resource e.g. a property file in the model layer or the view layer or an image which is part of the deployed application. As you don’t know where exactly the application is deployed, you can’t use the normal java.io.* classes as they require an absolute path to the file to read.

Implementation
In the Sample which is provided with this blog (see the link at the end of the blog) we read a properties file ‘model.properties’ in the model layer and make the values abailable via a service method in the application module. In the view layer we similar read another properties file ‘view.properties’ which we access through a bean method.

The UI consists of only one page which has two panelForms. Each has an inputText component where you can enter the key of the property you want to read, a button to activate a method to read the property and an outputText to show the result.

Running application

Running application

Model project resources:

# Model resources
ModuleName=BARTPAppModule
CreationDate=2012-08-01
EmptyKey=

ViewController project resources:

## Properties file for hte view controller
ViewControllerName=Hallo ik bin ein Berliner
CreateDate=2012-08-02

After entering a key value we get the values displayed:

Running application after getting some values

Running application after getting some values

Let’s start with the implementation in the model layer. In the application module implementation class we add a service method which is implemented as singleton (for the module), meaning that the resource is only loaded once and stored in the application module. Activation and passivation don’t need to be handled here, as the resource is read again after each activation as the local variable mModelProperties will be null after an activation.

public class BARFPAppModuleImpl extends ApplicationModuleImpl implements BARFPAppModule {
    private static ADFLogger _logger = ADFLogger.createADFLogger(BARFPAppModuleImpl.class);
    private static String PROPERTY_FILE = "de/hahn/blog/accessresourcesfrompath/model/resources/model.properties";
    Properties mModelProperties = null;

    /**
     * Returns the value of a property read from the model property file
     * @param key of the property to retrieve
     * @return value of the key or null if not found
     */
    public String getModelResource(String key) {
        // read property file only once
        if (mModelProperties == null) {
            initProperties();
        }

        return mModelProperties.getProperty(key);
    }

    /**
     * load the properties from the resource file
     */
    private void initProperties() {
        InputStream asStream = this.getClass().getClassLoader().getResourceAsStream(PROPERTY_FILE);
        if (asStream == null) {
            _logger.severe("Could not load property file: '" + PROPERTY_FILE + "'");
            mModelProperties = new Properties();
            return;
        }

        mModelProperties = new Properties();
        try {
            mModelProperties.load(asStream);
        } catch (IOException e) {
            e.printStackTrace();
            _logger.warning("Can't load properties: " + e.getMessage());
        }
        _logger.info("Model properties loaded!");
    }
...
}

The essential part is to use the class loader to read the resource as stream (line 24). The class loader allows us to read resources from the class path itself without knowing the absolute path in the file system. You only need to know the package structure and replace the ‘.’ which delimit the packages with the ‘/’. The class loader can then access all folders relative from its own load point. For more information check ClassLoader java doc.
The rest of the implementation is easy now. The same technique is used in the bean of the view controller project. Here we implement the method in a bean which we call on the click of a button ‘getViewReource’.

public class ResourceReaderBean {
    private static ADFLogger _logger =
        ADFLogger.createADFLogger(ResourceReaderBean.class);
    private Properties mViewProperties = null;
    private static String PROPERTY_FILE =
        "de/hahn/blog/accessresourcesfrompath/view/resource/view.properties";

    private void initProperties() {
        // instead of using the de.hahn.testproxy.backingbeans.test.properties you have to use de/hahn/testproxy/backingbeans/test.properties
        InputStream asStream =
            this.getClass().getClassLoader().getResourceAsStream(PROPERTY_FILE);
        if (asStream == null) {
            // file not found
            _logger.info("File not found: '" + PROPERTY_FILE + "'");
            return;
        }
        mViewProperties = new Properties();
        try {
            mViewProperties.load(asStream);
        } catch (IOException e) {
            e.printStackTrace();
        }
        if (!mViewProperties.isEmpty()) {
            _logger.info("Properties loaded: " + mViewProperties.toString());
        }

    }

    public void getViewResourceListner(ActionEvent actionEvent) {
        String value = "";
        // read property file only once
        if (mViewProperties == null)
            initProperties();

        BindingContainer bindingContainer =
            BindingContext.getCurrent().getCurrentBindingsEntry();
        AttributeBinding attrKey =
            (AttributeBinding)bindingContainer.getControlBinding("ViewResourceKey1");
        String key = (String)attrKey.getInputValue();
        if (mViewProperties != null && key != null) {
            String val = mViewProperties.getProperty(key);
            AttributeBinding attrValue =
                (AttributeBinding)bindingContainer.getControlBinding("ViewResourceValue1");
            attrValue.setInputValue(val);
        }
    }
...
}

The key to load from the resource is read from the binding variable ‘ViewResourceKey1’, the returned value is put back into the binding variable ‘ViewResourceValue1’. Both are defined in the variables section of the pageDef of the ShowProperty.jspx page.

You can download the sample from here BlogAccessResourcesFromPath.zip.
The sample was built with JDev 11.1.1.6.0 and uses the HR schema.

JDeveloper: Controlling which Resources (Files) are Copied into the Projects Output Directory

Some projects need resource files available in the output directory together with the class files. This is a fairly common task, however it’s not obvious how to archive this for all types of resources.

IF you never have had the problem, that a resource file you used in your project did not turn up in the output directory, you probably have used only default resource files like ‘properties’, ‘wsdl’ or ‘XML’ files. These files are copied into the output path by default.

Lets assume you have a resource e.g. a tiff file (*.tiff) or a TrueType font (*.ttf) which you need in your project and finally in the jar or adflib you build from the project. Lets add a dummy file ‘MyFont.ttf’ in a new folder named ‘res’ under the base path of the model projcect ‘de.hahn.blog.controlresourcefiles’. After this the model application looks like

Model Project after adding Font File

Model Project after adding Font File

No problem with this. Now if you compile the model project and check the output path Q:\QT\BlogControlResourceFiles\CRFModel\classes in this sample, you’ll notice that the folder ‘res’ and file ‘MyFont.ttf’ is not in the directory tree

Output Path after Compile

Output Path after Compile

If you check the path configuration of the project, it shows that the folder and the file should have been there

Project Path

Project Path

Well, the problem is an other configuration for the project. If you check the compiler node in the project properties you see that there is a filter which defines which file types are copied into the output path of the project

Project Compiler  Settings

Project Compiler Settings

As you see there is no entry for type ‘.ttf’ which means that files with this suffix are not copied into the output directory. The solution to the problem is to simply add the suffix ‘.ttf’ to the filter and compile the project again. After this the file ‘MyFont.ttf’ can be found in the projects output directory.

If you have any resource of a type (file suffix) you want to automatically copied into the projects output directory, you simple add the missing suffix to the filter.