Search icon CANCEL
Subscription
0
Cart icon
Your Cart (0 item)
Close icon
You have no products in your basket yet
Save more on your purchases! discount-offer-chevron-icon
Savings automatically calculated. No voucher code required.
Arrow left icon
Explore Products
Best Sellers
New Releases
Books
Events
Videos
Audiobooks
Packt Hub
Free Learning
Arrow right icon
timer SALE ENDS IN
0 Days
:
00 Hours
:
00 Minutes
:
00 Seconds

How-To Tutorials

7019 Articles
article-image-geronimo-architecture-part-2
Packt
13 Nov 2009
10 min read
Save for later

Geronimo Architecture: Part 2

Packt
13 Nov 2009
10 min read
Class loader architecture This section covers the class loader architecture for Apache Geronimo. The following image shows the class loader hierarchy for an application that is deployed in Apache Geronimo: The BootStrap class loader of the JVM is followed by the Extensions class loader and then the System class loader. The j2ee-system class loader is the primary class loader of Apache Geronimo. After the j2ee-system class loader, there are multiple other layers of class loaders before reaching the application class loaders. Applications have an application class loader, which loads any required application-level libraries and EJB modules. However, the web application packaged in the EAR will have its own class loader. The Administration Console has a ClassLoader Viewer portlet that can be used to view the class loader hierarchy as well as the classes loaded by each class loader. Modifying default class loading behavior In certain situations, we will need to follow a class loading strategy that is different from the default one that is provided by Apache Geronimo. A common situation where we need this functionality is when a parent configuration uses a library that is also used by the child and the library used by the parent is a different version, which is incompatible with the child's version of the library. In this case, if we follow the default class loading behavior, then we will always get the classes loaded by the parent configuration and will never be able to reference the classes in the library present in the child configuration. Apache Geronimo provides you with the ability to modify the default class loading behavior at the configuration level to handle such scenarios. This is done by providing certain elements in the deployment plan which, if present, will change the class loading behavior. These elements and the changes in class loading behavior that they represent, are explained as follows: hidden-classes: This tag is used to hide classes that are loaded in parent class loaders, so that the child class loader loads its own copy. Similarly, we can use this tag to specify the resources that should be loaded from the configuration class loader. For example, consider the case where you have a module that needs to load its copy of log4j. The server also has its own copy used for logging that is loaded in the parent class loader. We can add the hidden-classes element in the deployment plan for that module so that it loads its own copy of log4j, and the server loaded version of log4j is hidden from it. non-overridable-classes: This element specifies the list of classes that can be loaded only from the parent configurations of this configuration. In other words, the classes specified in this element cannot be loaded by the current configuration's class loader. The non-overridable-classes element is for preventing applications from loading their own copies of classes that should always be loaded from the parent class loaders, such as the Java EE API classes. private-classes: The classes that are defined by this tag will not be visible to class loaders that are the children of the current class loader. These classes will be loaded either from the current class loader or from its parents. The same class loading behavior can be achieved by using the hidden-classes tag in all of the child class loaders. inverse-classloading: If this element is specified, then the standard class loading strategy will be reversed for this module. This in effect means that a class is first looked up from the current class loader and then from its parent. Thus, the class loader hierarchy is inverted. suppress-default-environment: This will suppress the environment that is created by the builder for this module or configuration. This is a rarely-used element and can have nasty side effects if it is used carelessly. Important modules In this section, we will list the important configurations in Apache Geronimo. We will group them according to the Apache or other open source projects that they wrap. Configurations that do not wrap any other open source project will be listed under the Geronimo section. Apache ActiveMQ   org.apache.geronimo.configs/activemqbroker/2.1.4/car Apache Axis org.apache.geronimo.configs/axis/2.1.4/car org.apache.geronimo.configs/axis-deployer/2.1.4/car Apache Axis2 org.apache.geronimo.configs/axis2-deployer/2.1.4/car org.apache.geronimo.configs/axis2-ejb/2.1.4/car org.apache.geronimo.configs/axis2-ejb-deployer/2.1.4/car Apache CXF org.apache.geronimo.configs/cxf/2.1.4/car org.apache.geronimo.configs/cxf-deployer/2.1.4/car org.apache.geronimo.configs/cxf-ejb/2.1.4/car org.apache.geronimo.configs/cxf-ejb-deployer/2.1.4/car Apache Derby org.apache.geronimo.configs/derby/2.1.4/car Apache Geronimo org.apache.geronimo.configs/client/2.1.4/car org.apache.geronimo.configs/client-deployer/2.1.4/car org.apache.geronimo.configs/client-security/2.1.4/car org.apache.geronimo.configs/client-transaction/2.1.4/car org.apache.geronimo.configs/clustering/2.1.4/car org.apache.geronimo.configs/connector-deployer/2.1.4/car org.apache.geronimo.configs/farming/2.1.4/car org.apache.geronimo.configs/hot-deployer/2.1.4/car org.apache.geronimo.configs/j2ee-deployer/2.1.4/car org.apache.geronimo.configs/j2ee-server/2.1.4/car org.apache.geronimo.configs/javamail/2.1.4/car org.apache.geronimo.configs/persistence-jpa10-deployer/2.1.4/car org.apache.geronimo.configs/sharedlib/2.1.4/car org.apache.geronimo.configs/transaction/2.1.4/car org.apache.geronimo.configs/webservices-common/2.1.4/car org.apache.geronimo.framework/client-system/2.1.4/car org.apache.geronimo.framework/geronimo-gbeandeployer/2.1.4/car org.apache.geronimo.framework/j2ee-security/2.1.4/car org.apache.geronimo.framework/j2ee-system/2.1.4/car org.apache.geronimo.framework/jee-specs/2.1.4/car org.apache.geronimo.framework/jmx-security/2.1.4/car org.apache.geronimo.framework/jsr88-cli/2.1.4/car org.apache.geronimo.framework/jsr88-deploymentfactory/2.1.4/car org.apache.geronimo.framework/offline-deployer/2.1.4/car org.apache.geronimo.framework/online-deployer/2.1.4/car org.apache.geronimo.framework/plugin/2.1.4/car org.apache.geronimo.framework/rmi-naming/2.1.4/car org.apache.geronimo.framework/server-securityconfig/2.1.4/car org.apache.geronimo.framework/shutdown/2.1.4/car org.apache.geronimo.framework/transformeragent/2.1.4/car org.apache.geronimo.framework/upgrade-cli/2.1.4/car Apache Yoko org.apache.geronimo.configs/j2ee-corba-yoko/2.1.4/car org.apache.geronimo.configs/client-corba-yoko/2.1.4/car Apache Jasper org.apache.geronimo.configs/jasper/2.1.4/car org.apache.geronimo.configs/jasper-deployer/2.1.4/car JaxWS org.apache.geronimo.configs/jaxws-deployer/2.1.4/car org.apache.geronimo.configs/jaxws-ejb-deployer/2.1.4/car JSR 88 org.apache.geronimo.configs/jsr88-earconfigurer/2.1.4/car org.apache.geronimo.configs/jsr88-jarconfigurer/2.1.4/car org.apache.geronimo.configs/jsr88-rarconfigurer/2.1.4/car org.apache.geronimo.configs/jsr88-warconfigurer/2.1.4/car Apache MyFaces org.apache.geronimo.configs/myfaces/2.1.4/car org.apache.geronimo.configs/myfaces-deployer/2.1.4/car Apache OpenEJB org.apache.geronimo.configs/openejb/2.1.4/car org.apache.geronimo.configs/openejb-corbadeployer/2.1.4/car org.apache.geronimo.configs/openejb-deployer/2.1.4/car Apache OpenJPA org.apache.geronimo.configs/openjpa/2.1.4/car Spring org.apache.geronimo.configs/spring/2.1.4/car Apache Tomcat6 org.apache.geronimo.configs/tomcat6/2.1.4/car org.apache.geronimo.configs/tomcat6-clusteringbuilder-wadi/2.1.4/car org.apache.geronimo.configs/tomcat6-clusteringwadi/2.1.4/car org.apache.geronimo.configs/tomcat6-deployer/2.1.4/car org.apache.geronimo.configs/tomcat6-no-ha/2.1.4/car Apache WADI org.apache.geronimo.configs/wadi-clustering/2.1.4/car GShell org.apache.geronimo.framework/gshell-framework/2.1.4/car org.apache.geronimo.framework/gshell-geronimo/2.1.4/car Apache XmlBeans org.apache.geronimo.framework/xmlbeans/2.1.4/car Apache Pluto org.apache.geronimo.plugins/pluto-support/2.1.4/car     If you check the configurations, then you will see that most of the components that make up Geronimo have a deployer configuration and a main configuration. The deployer configuration contains the GBeans that will deploy modules onto that component. For example, the openejb-deployer contains GBeans that implement the functionality to deploy an EJB module onto Apache Geronimo. For accomplishing this, the EJB JAR file and its corresponding deployment plan are parsed by the deployer and then converted into a format that can be understood by the OpenEJB subsystem. This is then deployed on the OpenEJB container. The main configuration will usually contain the GBeans that configure the container and also manage its lifecycle. Server directory structure It is important for a user or an administrator to understand the directory structure of a Geronimo server installation. The directory structure of a v2.1.4 server is shown in the following screenshot: Please note that the directory that we will be referring to as <GERONIMO_HOME> is the geronimo-tomcat6-javaee5-2.1.4 directory shown in the screenshot. The following are some important directories that you should be familiar with: The bin directory contains the command scripts and the JAR files required to start the server, stop the server, invoke the deployer, and start the GShell. The etc directory contains the configuration files for GShell. The schema directory contains Geronimo schemas. The var/config directory contains Geronimo configurations files. A Geronimo administrator or user can find most of the configuration information about the server here. The var/derby directory contains the database files for the embedded Derby database server. The var/log directory contains logging configuration and logfiles. The var/security directory contains user credential and grouping files. The var/security/keystores directory contains the cryptographic keystore files used for server SSL configuration. The following are some important configuration files under the Geronimo directory structure: config.xml: This file is located under the &ltGERONIMO_HOME>/var/config directory. This file preserves the information regarding GBean attributes and references that were overridden from the default values used at deployment time. config-substitutions.properties: This file is located under the &ltGERONIMO_HOME>/var/config directory. The property values specified in this file are used in expressions in config.xml. The property values in this file can be overridden by using a system property or environment variable with a property name that is prefixed with org.apache.geronimo.config.substitution. artifact_aliases.properties: This file is located under the &ltGERONIMO_HOME>/var/config directory. This file is used to substitute one module or configuration ID for another module or configuration ID. The entries in this file are of the form oldArtifactId=newArtifactId, for example default/mylib//jar=default/mylib/2.0/jar. Note that the version number in the old artifact ID may be omitted, but the version number in the new artifact ID must be specified. client_artifact_aliases.properties: This file is located under the &ltGERONIMO_HOME>/var/config directory. This file is used for artifact aliasing with application clients. server-log4j.properties: This file is located under the &ltGERONIMO_HOME>/var/log directory. This file contains the logging configuration for the server. deployer-log4j.properties: This file is located under the &ltGERONIMO_HOME>/var/log directory. This file contains the logging configuration for the deployer. client-log4j.properties: This file is located under the &ltGERONIMO_HOME>/var/log directory. This file contains the logging configuration for application clients. users.properties: This file is located under the &ltGERONIMO_HOME>/var/security directory. This file contains the authentication credentials for the server. groups.properties: This file is located under the &ltGERONIMO_HOME>/var/security directory. This file contains the grouping information for the users defined in users.properties Among the directories that contain sensitive information, such as user passwords, are var/security, var/derby, and var/config. These directories should be protected using operating system provided directory and file security.
Read more
  • 0
  • 0
  • 1841

article-image-geronimo-architecture-part-1
Packt
13 Nov 2009
8 min read
Save for later

Geronimo Architecture: Part 1

Packt
13 Nov 2009
8 min read
Inversion of Control and dependency injection Inversion of Control (IoC) is a design pattern used in software engineering that facilitates the creation of loosely-coupled systems. In an IoC system, the flow of control is inverted, that is, the program is called by the framework—unlike in normal linear systems where the program calls the libraries. This allows us to circumvent the tight coupling that arises from the control being with the calling program. Dependency injection is a specific case of IoC where the framework provides an assembler or a configurator that provides the user program with the objects that it needs through injection. The user program declares dependencies on other services (provided by the framework or other user programs), and the assembler injects the dependencies into the user program wherever they are needed. It is important that you clearly understand the concept of dependency injection before we proceed further into the Geronimo architecture, as that is the core concept behind the functioning of the Geronimo kernel and how services are loosely coupled in it. To help you understand the concept more clearly, we will provide a simple example. Consider the following two classes: package packtsamples;public class RentCalculator{ private float rentRate; private TaxCalculator tCalc; public RentCalculator(float rate, float taxRate){ rentRate = rate; tCalc = new ServiceTaxCalculator(taxRate); } public void calculateRent(int noOfDays){ float totalRent = noOfDays * rentRate; float tax = tCalc.calculateTax(totalRent); totalRent = totalRent + tax; System.out.println("Rent is:"+totalRent); }}package packtsamples;public class ServiceTaxCalculator implements TaxCalculator{ private float taxRate; public ServiceTaxCalculator(float rate){ taxRate = rate; } public float calculateTax(float amount){ return (amount * taxRate/100); }}package packtsamples;public interface TaxCalculator{ public float calculateTax(float amount);}package packtsamples;public class Main { /** * @param args. args[0] = taxRate, args[1] = rentRate, args[2] =noOfDays */ public static void main(String[] args) { RentCalculator rc = new RentCalculator(Float.parseFloat(args[1]),Float.parseFloat(args[0])); rc.calculateRent(Integer.parseInt(args[2])); }} The RentCalculator class calculates the room rent including tax, given the rent rate, and the number of days. The TaxCalculator class calculates the tax on a particular amount, given the tax rate. As you can see from the code snippet given, the RentCalculator class is dependent on the TaxCalculator interface for calculating the tax. In the given sample, the ServiceTaxCalculator class is instantiated inside the RentCalculator class. This makes the two classes tightly coupled, so that we cannot use the RentCalculator with another TaxCalculator implementation. This problem can be solved through dependency injection. If we apply this concept to the previous classes, then the architecture will be slightly different. This is shown in the following code block:> package packtsamples.di;public class RentCalculator{ private float rentRate; private TaxCalculator tCalc; public RentCalculator(float rate, TaxCalculator tCalc){ rentRate = rate; this.tCalc = tCalc; } public void calculateRent(int noOfDays){ float totalRent = noOfDays * rentRate; float tax = tCalc.calculateTax(totalRent); totalRent = totalRent + tax; System.out.println("Rent is:" +totalRent); }}package packtsamples.di;public class ServiceTaxCalculator implements TaxCalculator{ private float taxRate; public ServiceTaxCalculator(float rate){ taxRate = rate; } public float calculateTax(float amount){ return (amount * taxRate/100); }}package packtsamples.di;public interface TaxCalculator{ public float calculateTax(float amount);} Notice the difference here from the previous implementation. The RentCalculator class has a TaxCalculator argument in its constructor. The RentCalculator then uses this TaxCalculator instance to calculate tax by calling the calculateTax method. You can pass in any implementation, and its calculateTax method will be called. In the following section, we will see how to write the class that will assemble this sample into a working program. package packtsamples.di;import java.lang.reflect.InvocationTargetException;public class Assembler { private TaxCalculator createTaxCalculator(String className, float taxRate){ TaxCalculator tc = null; try { Class cls = Class.forName(className); tc = (TaxCalculator)cls.getConstructors()[0] .newInstance(taxRate); } catch (ClassNotFoundException e) { e.printStackTrace(); } catch (IllegalArgumentException e) { e.printStackTrace(); } catch (SecurityException e) { e.printStackTrace(); } catch (InstantiationException e) { e.printStackTrace(); } catch (IllegalAccessException e) { e.printStackTrace(); } catch (InvocationTargetException e) { e.printStackTrace(); } return tc; } private RentCalculator createRentCalculator(float rate, TaxCalculator tCalc){ return new RentCalculator(rate,tCalc); } private void assembleAndExecute(String className, float taxRate, float rentRate, int noOfDays){ TaxCalculator tc = createTaxCalculator(className, taxRate); createRentCalculator(rentRate, tc).calculateRent(noOfDays);} /** * * @param args args[0] = className, args[1] = taxRate args[2] = rentRate args[3] = noOfDays */ public static void main(String[] args){ new Assembler().assembleAndExecute(args[0], Float.parseFloat(args[1]), Float.parseFloat(args[2]), Integer.parseInt(args[3])); }} In the given sample code, you can see that there is a new class called the Assembler. The Assembler, in its main method, invokes the implementation class of TaxCalculator that we want RentCalculator to use. The Assembler then instantiates an instance of RentCalculator, injects the TaxCalculator instance of the type we specify into it, and calls the calculateRent method. Thus the two classes are not tightly coupled and the program control lies with the assembler, unlike in the previous case. Thus there is Inversion of Control happening here, as the framework (Assembler in this case) is controlling the execution of the program. This is a very trivial sample. We can write an assembler class that is more generic and is not even coupled to the interface as in the previous case. This is an example of dependency injection. An injection of this type is called constructor injection, where the assembler injects values through the constructor. You can also have other types of dependency injection, namely setter injection and field injection. In the former, the values are injected into the object by invoking the setter methods that are provided by the class, and in the latter, the values are injected into fields through reflection or some other method. The Apache Geronimo kernel uses both setter injection and constructor injection for resolving dependencies between the different modules or configurations that are deployed in it. The code for these examples is provided under di-sample in the samples. To build the sample, use the following command: mvn clean install To run the sample without dependency injection, use the following command: java –cp di-sample-1.0.jar packtsamples.Main <taxRate> <rentRate><noOfDays> To run the sample with dependency injection, use the following command: java –cp di-sample-1.0.jar packtsamples.Assembler packtsamples.di.ServiceTaxCalculator <taxRate> <rentRate> <noOfDays> GBeans A GBean is the basic unit in Apache Geronimo. It is a wrapper that is used to wrap or implement different services that are deployed in the kernel. GBeans are similar to MBeans from JMX. A GBean has attributes that store its state and references to other GBeans, and can also register dependencies on other GBeans. GBeans also have lifecycle callback methods and metadata. The Geronimo architects decided to invent the concept of GBeans instead of using MBeans in order to keep the Geronimo architecture independent from JMX. This ensured that they did not need to push in all of the functionality required for the IoC container (that forms Geronimo kernel) into the JMX implementation. Even though GBeans are built on top of MBeans, they can be moved to some other framework as well. A user who is writing a GBean has to follow certain conventions. A sample GBean is shown below: import org.apache.geronimo.gbean.GBeanInfo;import org.apache.geronimo.gbean.GBeanInfoBuilder;import org.apache.geronimo.gbean.GBeanLifecycle;public class TestGBean implements GBeanLifecycle{ private String name; public TestGBean(String name){ this.name = name; } public void doFail() { System.out.println("Failed............."); } public void doStart() throws Exception { System.out.println("Started............"+name); } public void doStop() throws Exception { System.out.println("Stopped............"+name); } public static final GBeanInfo GBEAN_INFO; static { GBeanInfoBuilder infoBuilder = GBeanInfoBuilder .createStatic(TestGBean .class, "TestGBean"); infoBuilder.setPriority(2); infoBuilder.addAttribute("name", String.class, true); infoBuilder.setConstructor(new String[]{"name"}); GBEAN_INFO = infoBuilder.getGBeanInfo(); } public static GBeanInfo getGBeanInfo() { return GBEAN_INFO; }} You will notice certain characteristics that this GBean has from the previous section. We will list these characteristics as follows: All GBeans should have a static getGBeanInfo method, which returns aGBeanInfo object that describes the attributes and references of GBean as well as the interfaces it can implement. All GBeans will have a static block where a GBeanInfoBuilder object is created and linked to that GBean. All of the metadata that is associated with this GBean is then added to the GBeanInfoBuilder object. The metadata includes descriptions of the attributes, references, interfaces, and constructors of GBean. We can add GBeans to configurations either programmatically, using methods exposed through the configuration manager and kernel, or by making an entry in the plan for the GBean, as follows: <gbean name="TestGBean" class="TestGBean"> <attribute name="name">Nitya</attribute></gbean> We need to specify the attribute values in the plan, and the kernel will inject those values into the GBean at runtime. There are three attributes for which we need not specify values. These are called the magic attributes, and the kernel will automatically inject these values when the GBeans are being started. These attributes are abstractName, kernel, and classLoader. As there is no way to specify the values of these attributes in the deployment plan (an XML file in which we provide Geronimo specific information while deploying a configuration), we need not specify them there. However, we should declare these attributes in the GBeanInfo and in the constructor. If the abstractName attribute is declared, then the Geronimo kernel will inject the abstractName of the GBean into it. If it is the kernel attribute, then a reference to the kernel that loaded this GBean is injected. If we declare classLoader, then the class loader for that configuration is injected.
Read more
  • 0
  • 0
  • 1523

article-image-geronimo-plugins
Packt
12 Nov 2009
11 min read
Save for later

Geronimo Plugins

Packt
12 Nov 2009
11 min read
Developing a plugin In this section, we will develop our very own plugin, the World Clock plugin. This is a very simple plugin that provides the time in different locales. We will go through all of the steps required to develop it from scratch. These steps are as follows: Creating the plugin project Generating the plugin project, using maven2 Writing the plugin interface and implementation Creating a deployment plan Installing the plugin Creating a plugin project There are many ways in which you can develop plugins. You can manually create all of the plugin artifacts and package them. We will use the easiest method, that is, by using Maven's geronimo-plugin-archetype. This will generate the plugin project with all of the artifacts with the default values filled in. To generate the plugin project, run the following command: mvn archetype:create -DarchetypeGroupId=org.apache.geronimo.buildsupport -DarchetypeArtifactId=geronimo-plugin-archetype -DarchetypeVersion=2.1.4 -DgroupId=com.packt.plugins -DartifactId=WorldClock This will create a plugin project called WorldClock. A directory called WorldClock will be created, with the following artifacts in it: pom.xml pom.sample.xml src/main/plan/plan.xml src/main/resources In the same directory in which the WorldClock directory is created, you will need to create a java project that will contain the source code of the plugin. We can create this by using the following command: mvn archetype:create -DgroupId=com.packt.plugins -DartifactId=WorldClockModule This will create a java project with the same groupId and artifactId in a directory called WorldClockModule. This directory will contain the following artifacts: pom.xml src/main/java/com/packt/plugins/App.java src/test/java/com/packt/plugins/AppTest.java You can safely remove the second and third artifacts, as they are just sample stubs generated by the archetype. In this project, we will need to modify the pom.xml to have a dependency on the Geronimo kernel, so that we can compile the GBean that we are going to create and include in this module. The modified pom.xml is shown below: <project xsi_schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd"> <modelVersion>4.0.0</modelVersion> <groupId>com.packt.plugins</groupId> <artifactId>WorldClockModule</artifactId> <packaging>jar</packaging> <version>1.0-SNAPSHOT</version> <name>WorldClockModule</name> <url>http://maven.apache.org</url> <dependencies> <dependency> <groupId>junit</groupId> <artifactId>junit</artifactId> <version>3.8.1</version> <scope>test</scope> </dependency> <dependency> <groupId>org.apache.geronimo.framework</groupId> <artifactId>geronimo-kernel</artifactId> <version>2.1.4</version> </dependency> </dependencies></project> For simplicity, we have only one GBean in our sample. In a real world scenario, there may be many GBeans that you will need to create. Now we need to create the GBean that forms the core functionality of our plugin. Therefore, we will create two classes, namely, Clock and ClockGBean. These classes are shown below: package com.packt.plugins;import java.util.Date;import java.util.Locale;public interface Clock { public void setTimeZone(String timeZone); public String getTime();} and package com.packt.plugins;import java.text.DateFormat;import java.text.SimpleDateFormat;import java.util.Calendar;import java.util.Date;import java.util.GregorianCalendar;import java.util.Locale;import java.util.TimeZone;import org.apache.geronimo.gbean.GBeanInfo;import org.apache.geronimo.gbean.GBeanInfoBuilder;import org.apache.geronimo.gbean.GBeanLifecycle;import sun.util.calendar.CalendarDate;public class ClockGBean implements GBeanLifecycle, Clock{ public static final GBeanInfo GBEAN_INFO; private String name; private String timeZone; public String getTime() { GregorianCalendar cal = new GregorianCalendar(TimeZone. getTimeZone(timeZone)); int hour12 = cal.get(Calendar.HOUR); // 0..11 int minutes = cal.get(Calendar.MINUTE); // 0..59 int seconds = cal.get(Calendar.SECOND); // 0..59 boolean am = cal.get(Calendar.AM_PM) == Calendar.AM; return (timeZone +":"+hour12+":"+minutes+":"+seconds+":"+((am)? "AM":"PM")); } public void setTimeZone(String timeZone) { this.timeZone = timeZone; } public ClockGBean(String name){ this.name = name; timeZone = TimeZone.getDefault().getID(); } public void doFail() { System.out.println("Failed............."); } public void doStart() throws Exception { System.out.println("Started............"+name+" "+getTime()); } public void doStop() throws Exception { System.out.println("Stopped............"+name); } static { GBeanInfoBuilder infoFactory = GBeanInfoBuilder.createStatic ("ClockGBean",ClockGBean.class); infoFactory.addAttribute("name", String.class, true); infoFactory.addInterface(Clock.class); infoFactory.setConstructor(new String[] {"name"}); GBEAN_INFO = infoFactory.getBeanInfo(); } public static GBeanInfo getGBeanInfo() { return GBEAN_INFO; }} As you can see, Clock is an interface and ClockGBean  is a GBean that implements this interface. The Clock interface exposes the functionality that is provided by the ClockGBean. The doStart(),   doStop(), and doFail()  methods are provided by the GBeanLifeCycle interface, and provide lifecycle callback functionality. The next step is to run Maven to build this module. Go to the command prompt, and change the directory to the WorldClockModule directory. To build the module, run the following command: mvn clean install Once the build completes, you will find a WorldClockModule-1.0-SNAPSHOT.jar in the WorldClockModule/target directory. Now change the directory to WorldClock, and open the generated pom.xml file. You will need to uncomment the deploymentConfigs for the gbeanDeployer, and add the following module that you want to include in the plugin: <module> <groupId>com.packt.plugins</groupId> <artifactId>WorldClockModule</artifactId> <version>1.0</version> <type>jar</type></module> You will notice that we are using the car-maven-plugin in the pom.xml file. The car-maven-plugin is used to build Apache Geronimo configuration archives without starting the server. The final step is to create the deployment plan in order to deploy the module that we just created into the Apache Geronimo server. This deployment plan will be used by the car-maven-plugin to actually create the artifacts that will be created during deployment to Apache Geronimo. The deployment plan is shown below: <module > <environment> <moduleId> <groupId>com.packt.plugins</groupId> <artifactId>WorldClock</artifactId> <version>1.0</version> <type>car</type> </moduleId> <dependencies/> <hidden-classes/> <non-overridable-classes/> <private-classes/> </environment> <gbean name="ClockGBean" class="com.packt.clock.ClockGBean"> <attribute name="name">ClockGBean</attribute> </gbean></module> Once the plan is ready, go to the command prompt and change the directory to the WorldClock directory. Run the following command to build the plugin: mvn clean install You will notice that the car-maven-plugin is invoked and a WorldClock-1.0-SNAPSHOT.car file is created in the WorldClock/target directory. We have now completed the steps required to create an Apache Geronimo plugin. In the next section, we will see how we can install the plugin in Apache Geronimo. Installing a plugin We can install a plugin in three different ways. One way is to   use the deploy.bat or deploy.sh script, another way is to use the install-plugin command in GShell, and the third way is to use the Administration Console to  install a plugin from a plugin repository. We will discuss each of these methods: Using deploy.bat or deploy.sh file: The deploy.bat or deploy.sh script is found in the <GERONIMO_HOME>/bin directory. It has an option install-plugin, which can be used to install plugins onto the server. The command syntax is shown below: deploy install-plugin <path to the plugin car file> Running this command, and passing the path to the plugin .car archive on the disk, will result in the plugin being installed onto the Geronimo server. Once the installation has finished, an Installation Complete message will be displayed, and the command will exit. Using GShell: Invoke  the gsh command from the command prompt, after changing the current directory to <GERONIMO_HOME>/bin. This will bring up the GShell prompt. In the GShell prompt, type the following command to install the plugin: deploy/install-plugin <path to the plugin car file> Please note that, you should escape special characters in the path by using a leading "" (back slash) before the character. Another way to install plugins that are available in remote plugin repository is by using the list-plugins command. The syntax of this command is as given below: deploy/list-plugins <URI of the remote repository> If a remote repository is not specified, then the one configured in Geronimo will be used instead. Once this command has been invoked, the list of available plugins in the remote repository is shown, along with their serial numbers, and you will be prompted to enter a comma separated list of the serial numbers of the plugins that you want to install. Using the Administration Console: The Administration Console has a Plugins portlet that can be used to list the plugins available in a repository specified by the user. You can use the Administration Console to select and install the plugins that you want from this list. This portlet also has the capability to export applications or services in your server instance as Geronimo plugins, so that they can be installed on other server instances. See the Plugin portlet section for details of the usage of this portlet. Available plugins The  web  site http://geronimoplugins.com/ hosts Apache Geronimo plugins. It has many plugins listed for Apache Geronimo. There are plugins for Quartz, Apache Directory Server, and many other popular software packages. However, they are not always available for the latest versions of Apache Geronimo. A couple of fairly up-to-date plugins that are available for Apache Geronimo are the Windows Service Wrapper plugin and the Apache Tuscany plugin for Apache Geronimo. The Windows Service Wrapper provides the ability for Apache Geronimo to be registered as a windows service. The Tuscany plugin is an implementation of the SCA Java EE Integration specification by integrating Apache Tuscany as an Apache Geronimo plugin. Both of these plugins are available from the Apache Geronimo web site. Pluggable Administration Console Older versions of Apache Geronimo came with a monolithic Administration Console. However, the server was extendable through plugins. This introduced a problem: How to administer the new plugins that were added to the server? To resolve this problem, the Apache Geronimo developers rewrote the Administration Console to be extensible through console plugins called Administration Console Extensions. In this section, we will look into how to create an Administration Console portlet for the World Clock plugin that we developed in the previous section. Architecture The pluggable Administration Console functionality is based on the support provided by the Apache Pluto portlet container for dynamically adding and removing portlets and pages without requiring a restart. Apache Geronimo exposes this functionality through two GBeans, namely, the Administration Console Extension (ACE) GBean  (org.apache.geronimo.pluto.AdminConsoleExtensionGBean) and the Portal Container Services GBean (org.apache.geronimo.pluto. PortalContainerServicesGBean). The PortalContainerServicesGBean exposes the features of the Pluto container in order to add and remove portlets and pages at runtime. The ACE GBean invokes these APIs to add and remove the portlets or pages. The  ACE GBean should be specified in the Geronimo-specific deployment plan of your web application or plugin, that is, geronimo-web.xml. The architecture is shown in the following figure: Developing an Administration Console extension We will now go through the steps to develop an Administration Console Extension for the World Clock plugin that we created in the previous section. We will use Maven WAR archetype to create a web application project. To create the project, run the following command from the command-line console: mvn archetype:create -DgroupId=com.packt.plugins -DartifactId=ClockWebApp -DarchetypeArtifactId=maven-archetype-webapp This will result in the Maven web project being created, named ClockWebApp. A default pom.xml will be created. This will need to be edited to add dependencies to the two modules, as shown in the following code snippet: <dependency> <groupId>org.apache.geronimo.framework</groupId> <artifactId>geronimo-kernel</artifactId> <version>2.1.4</version></dependency><dependency> <groupId>com.packt.plugins</groupId> <artifactId>WorldClockModule</artifactId> <version>1.0</version></dependency> We add these dependencies because the portlet that we are going to write will use the classes mentioned in the above two modules.
Read more
  • 0
  • 0
  • 2381

article-image-short-tour-through-nav-2009-part-3
Packt
11 Nov 2009
17 min read
Save for later

A Short Tour through NAV 2009: Part 3

Packt
11 Nov 2009
17 min read
An introduction to NAV development As part of our study of NAV development tools and techniques, we are going to do some exercises here and there. There's no better way to learn than to try out the tools in an environment where you can't break anything important. We're going to do development work for a simple system. As discussed earlier, we will access C/SIDE through the Tools  Object Designer| menu option, as shown in the following screenshot: Our scenario for development exercises Our organization is a small, not-for-profit one. The name is ICAN, which stands for International Community And Neighbors. We collect materials, mostly food, from those who can afford to share and distribute it to those who have difficulty providing for themselves or their families. A large company has supplied us with a small computer network and a three-user Microsoft Dynamics NAV 2009 system. We need to track the donations which come in. They may be items, money, or some type of in-kind services. If they are items, we will need to inventory them. We also need to track donors, volunteers, and the people on our "help list" (our clients). A variety of reports and inquiries will be needed. As with any application, new requirements are likely to arise as time goes by. And, in this case, since the purpose of our scenario is to act as a laboratory for experimenting with NAV development tasks, new requirements will arise so that we can do additional experiments. Getting started with application design Our design for the ICAN application will start with the initial design of a Donor table, a Donor Card Page, a Donor List Page, and a Donor List Report. Along the way, we will review the basics of each of these NAV object types. Application tables Table objects are the foundation of every NAV application. Every project should start by designing the tables. Tables contain the definitions of the data structures, the data relationships within and between the tables, as well as many of the data constraints and validations. The coded logic in a table triggers not only provides the basic control for the insertion, modification, and deletion of records, but also embodies many of the business rules for an application. Such logic isn't just at the record level but also at the field level. When we embed a large part of the application data handling logic within the tables, it makes the application easier to develop, debug, support, modify, and upgrade. Designing a simple table Let's create a simple Donor table for our NAV Donor application. First, we will inspect existing definitions for tables containing name and address information. Basic inspection is done by clicking on the Tables button in the Object Designer, then highlighting the desired table and clicking the Design button. Good examples are the Customer table (table object 18) and the Vendor table (table object 23). We see some field name and definition patterns that we will generally copy on the basis of "consistency is good". One exception to the copying will be the Primary Key field. In the other tables, that field is No., but we will use Donor ID, just to make all our examples stand out from views of standard code. The Donor table will contain the following data fields: Field names Definitions Donor ID 20 character text (code) Name 50 character text Address 50 character text Address 2 50 character text City 30 character text State/Provinc 10 character text Post code 20 character text (code) Country/Region code 10 character text (code) In the preceding data field list, three of the fields are defined as Code fields, rather than Text fields. This is because these will be referenced by or will reference to other data tables. Using Code fields limits the contents to a subset of the ASCII character set, making it easier to constrain entries to a range of values. The Donor ID will be a unique identifier for our Donor record as it will also be referenced by other subordinate tables. The Post Code and Country/Region Code will reference other existing tables for validation. We choose the name, size, and data definition of these last two fields based on inspecting the equivalent field definitions in the Customer and Vendor tables. We will have to design and define any referenced validation tables before we can eventually complete the definition of the Donor table. But our goal at the moment is just to get started. Creating a simple table Open the Object Designer, click on Table (in the left column of buttons) and click on New (in the bottom row of buttons). Enter the first field name (Donor ID) in the Field Name column and then enter the data type in the Data Type column. For those data types where length is appropriate, enter the maximum length in the Length column (for example text fields require lengths, date or numeric fields do not). Enter Description data as desired; these are only for display here as internal documentation. As you can see in the following screenshot (and will have noticed already if you are following along in your system), when you enter a Text data type, the field length will default to 30 characters. This is simply an 'ease-of-use' default, which you should override as appropriate for your design. The 30 character Text default and 10 character Code default are used because this matches many standard application data fields of those data types. Field numbering The question often arises as to what field numbering scheme to use. Various systems follow a variety of standard practices, but in NAV, when you are creating a new table from scratch, it is a good idea to increment the Field No. by 10, as you can see in the preceding screenshot. The default increment for Field No. is 1. For a group of fields (such as an address block) where you are certain you will never add any intervening fields, you could leave the increment at 1, but there is no cost for using the larger increment. The numeric sequence of fields determines the default sequence in which data fields will display in a wide variety of situations, especially in the Classic Client. An example would be the order of the fields in any list presented to the user for setting up data filters in the Classic Client. This default sequence can only be changed by renumbering the fields. The compiler references each field by its Field No. not by its Field Name, so the renumbering of fields can be difficult once you have created other routines that reference back to these fields. At that point, it is generally better to simply add new fields where you can fit them without any renumbering. General rule: Once a design has started to mature, don't renumber data fields. In fact, it can be irritatingly painful to renumber fields at any point after a table has been defined and saved. In addition to the field numbers controlling the sequence of presentation of fields, the field numbers control bulk data transfer (those transfers that operate at the record level rather than explicitly field to field transfer—for example the TRANSFERFIELD instruction). In a record-level transfer, data is transferred from each field in the source record to the field of the same number in the target record. Obviously, it's a good idea to define an overall standard for field numbering as you start. Doing so makes it easier to plan your field numbering scheme for each table. Your design will be clearer for you and your user, if you are methodical about your design planning before you begin writing code (try to avoid the Ready-Fire-Aim school of system development). The increment of Field No. by 10 allows you to insert new fields in their logical sequence as the design matures. While it is not required to have the data fields appear in any particular order, it is frequently convenient for testing and often clarifies some of the user interactions. When you have completed this first table, your definition should look like the following screenshot: At this point, you can exit and save your Donor Table. The easiest way to do this is to simply press Esc until you are asked to save your changes. When you respond by clicking on Yes, you will be asked for the object number and name you wish to assign. In a normal development situation, you will plan ahead what Object Number and descriptive Object Name you want to use. In this case, we will use table Object No. 50000 and name it Donor. We are using 50000 as our Table Number just because it is the first (lowest) number available to us for a custom table through our Table Designer granule license. Note that NAV likes to compile any object as it is saved, so the Compiled option is automatically checked. A compiled object is one that can be executed. If the object we were working on was not ready to be compiled without error, we could unselect the Compiled option in the Save As window, as shown in the preceding screenshot. Be careful, as uncompiled objects will not be considered by C/SIDE when changes are made to other objects. Until you have compiled an object, it is a "work in progress", not an operable routine. There is a Compiled flag on every object that gives its compilation status. Even when you have compiled an object, you have not confirmed that all is well. You may have made changes that affect other objects which reference the modified object. As a matter of good work habit, recompile all objects before you end work for the day. Pages/Forms Pages are for the Role Tailored Client. Forms are for the Classic Client. If you are in the challenging position of maintaining a dual personality system (that is, running both clients), you will be forced to maintain equivalent versions of both Pages and Forms. We aren't going to address that situation in depth in this article as we are not going to cover the design and development of Forms in detail. In this article, we will focus on Pages and cover Forms just enough so that you can create a few to use as maintenance and debugging tools. Pages fulfill several basic purposes. They provide views of data or processes designed for on-screen display only. They provide key points of user data entry into the system. They also act as containers for action items (menu options). The first page with which a user will come in contact is a Role Center Page. The Role Center Page provides the user with a view of work tasks to be done. The Role Center Page should be tailored to the job duties of each user, so there will be a variety of Role Center Page formats for any firm. There are several basic types of display/entry pages in NAV: List pages Card pages Document pages Journal/Worksheet pages There are also page parts (they look and program like a page, but don't stand alone) as well as user interfaces that appear as pages, but are not Page objects. The latter user interfaces are generated by various dialog functions. List pages List pages display a simple list of any number of records in a single table. The Customer List page in the following screenshot shows a subset of the data for each customer displayed. The Master record list shows fields intended to make it easy to find a specific entry. List pages/forms often do not allow entry or editing of the data. Journal/Worksheet pages look like List pages, but are intended for data entry.   Card pages Card pages display one record at a time. These are generally used for the entry or display of individual table records. Examples of frequently accessed card pages include Customer Card for customer data, Item Card for inventory items, and G/L Account Card for general ledger accounts. Card pages often have FastTabs (multiple pages with each tab focusing on a different set of related customer data). FastTabs can be expanded or collapsed dynamically, allowing the specific data visible at any time to be controlled by the user. Card pages for Master records display all the required data entry fields. Typically, they also display summary data about related activity so that the Card page can be used as the primary inquiry point for Master records. The following screenshot is a sample of a standard Customer Card:   Document pages Another page style within NAV consists of what looks like a Card page plus a List page. An example is the Sales Order page as shown in the following screenshot. In this example, the upper portion of the page is in the style of a Card page with several tabs showing Sales Order data fields that have a single occurrence on the page (in other words, do not occur in a repeating column). The lower portion of the Document page is in the style of a List page showing a list of all the line items on the Sales Order (all fields are in repeating columns). Line items may include product to be shipped, special charges, comments, and other pertinent order details. The information to the right of the data entry is related data and computations that have been retrieved and formatted. On top of the page, the information is for the Ordering customer and the bottom right contains information for the item on the selected line.   Journal/Worksheet pages Journal and Worksheet pages look very much like List pages. They display a list of records in the body of the page. Many have a section at the bottom that shows details about the selected line and/or totals for the displayed data. These pages may also include a Filter pane and perhaps a FactBox. The biggest difference between Journal/Worksheet pages and basic List pages is that Journal and Worksheet pages are designed to be used for data entry. An example of a Worksheet page, the Requisition Worksheet in Purchasing, is shown in the following screenshot. This Worksheet assists the user in determining and defining what purchases should be made. Standard elements of pages A page consists of Page properties and triggers, Controls, Control properties and triggers. Data controls generally are either labels displaying constant text or graphics, or containers that display data or other controls. Controls can also be elements such as buttons, action items, and page parts. While there are a few instances where you must include C/AL code within page or page control triggers, in general it is good practice to minimize the amount of code embedded within pages. Most of the time, any data-related C/AL code can (and should) be located within the table object rather than the page object. Creating a Card page Let us try creating a Card page and a List page for the table we created earlier. The NAV IDE contains object generation tools (Wizards) to help you create relatively fully formed Classic Client forms and reports. Creating pages and reports for the Role Tailored Client is less about Wizards and more about actually using the Designer tools, but the Wizards are still very useful. Open the Object Designer, click on Page and then click on New. The Page Wizard's screen will appear. Enter the name (Donor) or number (50000) of the Table with which the page is to be associated (bound). Choose the option Create a page using a wizard:. Choose Card as shown in the following screenshot. Click on OK. Next, you will encounter the following Card Page Wizard screen: As this is our first Page creation effort, we will keep it simple. We will accept the single suggested FastTab of General and click on Next to proceed. That will take us to the following screen. Now it's time to add all the data fields for our Donor table to the page. Click on the button shown in the following image. That will move all of the Available Fields to the Field Order window. Now click on the Finish button. The Page wizard will close and you will be looking at the Page Designer form showing the generated page code, as shown in the following screenshot. Place your cursor on the first blank line at the bottom of the list of control definitions. Right-click and select Properties to open the Page-Properties screen as shown below (you could also press Shift + F4 or click on the Properties icon at the top of the screen). Type the page name to be displayed "Donor Card" into the Caption field, it will default into the MLCaption field too, as follows: Escape from that screen, and Escape from the Page Designer screen. A Save Changes form will be displayed, just like the one we saw when we finished defining our Donor table. We have the same task now as we did then, to assign an ID (object number) and Name. Let's make this Page number 50000 and call it Donor Card. Leave the Compiled checkbox checked and click on OK so that the Page will be compiled (and error checked). Assuming you haven't received any error messages, you have now created your first NAV 2009 page object. Congratulations! If you are doing your work with the original release of NAV 2009, you have to test a new card page by running it from the system Run option. From the Start menu, select Run. In the command box, enter the following: Dynamicsnav:////runpage?page=50000 If you are using NAV 2009 SP1 or later, you can test your page by simply highlighting it and clicking on the Run button at the bottom of the Object Designer screen (the Run button is programmed to automatically submit that long command to the system). This will invoke the Role Tailored Client, open it up, and call up your new page 50000. You should see a screen that looks like this. Experiment a little. Expand the card page to full screen. Enter some Donor data for two or three donors (hint: click on the Actions icon at the top of the page and choose the New option). You might want to open the page with the Page Designer using the Design button, add one of the data fields so it will appear a second time on the card. This time, change the Caption field for that data item. Save and compile. Run a test. Before you quit experimenting, go back and remove any extras that you had added, so that later your Donor Card will be what we created initially. You could create a second copy of the page so that you could experiment, then discard it later. First, open Page 50000 in Design mode. Then click on File  Save As| and save the page object as Page number 50090 with a different name such as Test Donor Card. Compile it, then make whatever changes you want, deleting the experimental page when you are done experimenting. When you begin testing, you will find that a lot of features have been created automatically as part of the Card page, but you are still very limited in what you can do. For example, without a properly set up List Page, it's harder to move around in your table and quickly find different Donors, especially if there are more than just a few records in the table. This difficulty would be even more significant for your users. That's why List pages are the primary data navigation interface in the application system. So let's create a List page for our Donor table.
Read more
  • 0
  • 0
  • 1563

article-image-short-tour-through-nav-2009-part-2
Packt
11 Nov 2009
8 min read
Save for later

A Short Tour through NAV 2009: Part 2

Packt
11 Nov 2009
8 min read
NAV 2009: A set of building blocks and development tools If you look at NAV 2009 from the point of view of a developer, you may see it as a set of customizable off-the-shelf program objects (the building blocks) plus the IDE which allows you to modify those objects and create new ones (the C/SIDE development tools). The NAV 2009 system is an object-based system, consisting of several thousand application objects, the building blocks, made up of the eight different object types available in NAV. NAV does not have all of the features of an object-oriented system. A full-featured object-oriented system would allow the definition and creation of new object types, while NAV only allows for the creation and modification of the predefined object types. NAV object types Let's start with some basic definitions of the object types that are part of NAV: Table: Tables are the definers and containers of data. Form: Forms are the screen display constructs for the Classic Client user interface. Page: Pages are the screen display constructs for the Role Tailored Client user interface. Pages are designed and rendered (displayed) using technology that is new to NAV 2009. Report: Reports allow the display of data to the user in "hardcopy" format, either onscreen (preview mode) or via a printer device. Report objects can also update data in processes with or without accompanying data display output. Dataport: Dataports allow the importing and exporting of data from/to external files in the Classic Client. XMLport: XMLports are similar to Dataports. In the Classic Client, XMLports are specific only to XML files and XML formatted data. In the Role Tailored Client, XMLports handle the tasks of both XMLports and Dataports. Codeunit: Codeunits are containers for code, always structured in code segments called functions. MenuSuite: MenuSuites contain menus which refer in turn to other types of objects. MenuSuites are structured differently from other objects, especially since they cannot contain any code or logic. In the Role Tailored Client, MenuSuites are translated into Navigation Pane menu entries. The C/SIDE Integrated Development Environment NAV includes a full set of software development tools. All NAV development tools are accessed through the C/SIDE Integrated Development Environment. This environment and its full complement of tools are generally just referred to as C/SIDE. C/SIDE includes the C/AL compiler. All NAV programming uses C/AL. No NAV development can be done without using C/SIDE. The C/SIDE Integrated Development Environment is referred to as the Object Designer within NAV. It is accessed through the Tools  Object Designer menu |option as shown in the following screenshot: The language in which NAV is coded is C/AL. A sample of some C/AL code is shown as follows. C/AL syntax has considerable similarity to Pascal. As with any programming language, readability is enhanced by careful programmer attention to structure, logical variable naming, process flow consistent with that of the code in the base product and good documentation both inside and outside of the code. Insert code1 A large portion of the NAV system is defined by tabular entries, properties, and references. This doesn't reduce the requirement for the developer to understand the code, but it does allow some very significant applications development work to be done on a more of a point-and-choose or fill-in-the-blank approach than the traditional "grind it out" approach to coding. NAV gives developers the ability to focus on design rather than code. Much of the time, the effort required to create a new NAV application or modification will be heavily weighted on the side of design time, rather than technical development time. This long term goal for systems development tools has always been true for NAV. As the tools mature, NAV development continues to be more and more heavily weighted to design time rather than coding time. Object Designer tool icons The following screenshot shows a list of Object Designer tool icons. These Object Designer icons are shown isolated in the screenshot and then described briefly in the following table. Some of these icons apply to actions that are only for objects which run in the Classic Client, some are for objects for both clients. Additional information is available in the C/SIDE Help files and the Microsoft NAV documentation. The following table lists the specific development tools used for each object type. Also, as shown in this table, some objects are limited to being used in the Classic Client, some are limited to being used in the RTC environment, some are used in both, and some are interpreted differently depending on the environment in which they are invoked. Object Type Design Tool User Interface Comments Table Table Designer Classic and RTC   Form Form Designer Classic   Page Page Designer RTC   Report Report Designer Classic (and RTC) If a report is run from the RTC but doesn't have an RTC compatible layout, NAV will run it under a temporary instance of the Classic Client   Report Designer (data definition) + Visual Studio (user interface) RTC   Dataport Dataport Designer Classic   XMLPort XMLport Designer Classic and RTC For the RTC, Dataport functionality is handled through appropriately defined XMLports Codeunit IDE code editor for the C/AL language Classic and RTC   MenuSuite Navigation Pane Designer Classic and RTC Much of the navigation in the RTC is done via Role Center pages rather than menus NAV object and system elements Let's take a look at the following: Database: NAV has two physical database options. One is the C/SIDE Database Server; the other is the Microsoft SQL Server. The C/SIDE Database Server, formerly known as the "Native" database, only supports the two-tier client. The NAV 2009 three-tier functionality is only compatible with a SQL Server implementation. You won't be surprised to know that one or two product versions in the future, the only database option will be SQL Server. At the basic application or code design levels, you don't care which database is being used. For sophisticated or high data volume applications you care a great deal about the underlying database strengths, weaknesses and features. Properties: These are the attributes of the element (for example object, data field, or control) that define some aspect of its behavior or use. For example, attributes such as display length, font type or size, and if the elements are either editable or viewable. Fields: These are the individual data items, defined either in a table or in the working storage of an object. Records: These are groups of fields (data items) that are handled as a unit in most Input/Output operations. The table data consists of rows of records with columns consisting of fields. Controls: These are containers for constants and data. A control corresponds to a User Interface element on a form/page or a report. The visible displays in reports and forms consist primarily of controls. Triggers: The generic definition is a mechanism that initiates (fires) an action when an event occurs and is communicated to the application object. A trigger is either empty or contains code that is executed when the associated event fires the trigger. Each object type has its own set of predefined triggers. The event trigger name begins with the word "On" such as OnInsert, OnOpenPage, and OnNextRecord. NAV triggers have some similarities to those in SQL, but they are not the same. NAV triggers are locations within the various objects where a developer can place comments or C/AL code. When you look at the C/AL code of an object in its Designer, the following have a physical resemblance to the NAV event based triggers: Documentation which can contain comments only, no executable code. Every object type except MenuSuite has a single Documentation section at the beginning. Functions which can be defined by the developer. They represent callable routines that can be accessed from other C/AL code either inside or outside the object where the called function resides. Many functions are provided as part of the standard product. As a developer, you may add your own custom functions as needed. Object numbers and field numbers: The object numbers from 1 (one) to 50,000 and in the 99,000,000 (99 million) range are reserved for use by NAV as part of the base product. Objects in this number range can be modified or deleted, but not created with a developer's license. Field numbers are often assigned in ranges matching the related object numbers (that is starting with 1 for fields relating to objects numbered 1 to 50,000, starting with 99,000,000 for fields in objects in the 99,000,000 and up number range). Object and field numbers from 50,001 to 99,999 are generally available to the rest of us for assignment as part of customizations developed in the field using a normal development license. But object numbers from 90,000 to 99,999 should not be used for permanent objects as those numbers are often used in training materials. Microsoft allocates other ranges of object and field numbers to Independent Software Vendor (ISV) developers for their add-on enhancements. Some of these (in the 14,000,000 range in North America, other ranges for other geographic regions) can be accessed, modified, or deleted, but not created using a normal development license. Others (such as in the 37,000,000 range) can be executed, but not viewed or modified with a typical development license. The following table summarizes the content as: Object Number range   Usage   1 - 9,999   Base-application objects   10,000 - 49,999   Country-specific objects   50,000 - 99,999   Customer-specific objects   100,000 - 98,999,999   Partner-created objects   Above 98,999,999 Microsoft territory
Read more
  • 0
  • 0
  • 1396

article-image-apache-continuum-ensuring-health-your-source-code-part-1
Packt
10 Nov 2009
14 min read
Save for later

Apache Continuum: Ensuring the Health of your Source Code (Part 1)

Packt
10 Nov 2009
14 min read
Knowing when your build breaks First, let us define a continuous integration, or CI server. A CI server is an application that is used to monitor your project's build and ensure the health of your source repository. In a software development setup, a project includes a number of developers. If it is a big project, expect the team to be larger as well. Apart from this, a team can be physically distributed in different locations. An open source project is a good example of this. Developers in open source communities are often located in different parts of the world. All these developers commit or check their code into a central source repository. Sometimes though, people only tend to build the component which they have made changes to (especially when building the entire project takes a while!). Moreover, when that component builds successfully, they commit their changes without checking whether there are dependent components that had been affected by these changes. Now, what if it turned out that one dependent component's build failed because of those changes? No one would know until someone from the team updated his or her local copy and ran a full build. How can everyone else proceed if they were then unable to build the project? If the developer who discovered the broken build were a Good Samaritan, he would try to fix the build on his own (which would probably take a while). Tracking down and identifying which changes caused the build to break is a tedious job. Either that or the developer would notify his team mates about the broken build so that whoever caused the build to fail would know and would be able to fix the build. However, what if the person who caused the build to break has just left for a vacation? His teammates could either track him down and bug him while he is on vacation, or they could fix it themselves. Either of which would eat up a lot of development time! If only they had their project source in a CI server: They would know that they broke some component in the project when the build failed—whether it was a compilation failure or a test failure, because a CI server ensures that the entire source code builds and all the tests pass. They would know that the build is broken because the CI server would notify them when it encountered the build failure. They would know who caused the build to break, and they can contact that person and help them to fix it. They would know which changes caused the build to break as this information is available in the CI server. They would know when there is a problem in their build and deployment infrastructure. For example, when their source repository is down or their deployment repository is not accessible. We did not see these issues in the scenario above but they do happen. It is obvious from our definition and list above what the importance of a CI server is. However, we will see in the following sections that it's useful for more than just that. Setting up Continuum Continuum is an open source continuous integration server that has support for different types of projects such as Maven 2, Maven 1, Ant, and shell projects. Some of its features include build automation, release management, source control management, and build statistics. Archiva and Continuum used to be sub-projects of Maven. Continuum officially became a top-level project at the ASF in February 2008, just a couple of months before Archiva. Setting up a source repository We start off by setting up our source repository where we will be adding the example application that we have been utilizing. The source repository server that we will be using is Subversion or SVN for short. More details about Subversion can be found at http://subversion.tigris.org/. First, let's download and install Subversion as specified from their site http://subversion.tigris.org/getting.html#binary-packages. We can then set up our local SVN repository by executing the following command: $ svnadmin create /path/to/data/svn Make sure that you replace /path/to from the above command with the actual directory path where you will put your SVN data. This applies to all the commands with the same convention in this article. Before we add our example application centrepoint and its parent POM effectivemaven-parent in the SVN repository, let's first create the appropriate directories in the source repository by executing the following commands: $ svn mkdir file://localhost/path/to/data/svn/effectivemaven-parent/$ svn mkdir file://localhost/path/to/data/svn/effectivemaven-parent/trunk$ svn mkdir file://localhost/path/to/data/svn/effectivemaven-parent/ branches$ svn mkdir file://localhost/path/to/data/svn/effectivemaven-parent/tags$ svn mkdir file://localhost/path/to/data/svn/centrepoint/$ svn mkdir file://localhost/path/to/data/svn/centrepoint/trunk$ svn mkdir file://localhost/path/to/data/svn/centrepoint/branches$ svn mkdir file://localhost/path/to/data/svn/centrepoint/tags The trunk/ directory is where the latest or current development happens. To use software lingo, this is the bleeding edge. The branches/ directory contains development code of certain versions of the project for maintenance purposes. It may also contain temporary copies of the source code in trunk/ specifically for developing major features or fixing critical bugs that may possibly break the code (they eventually get merged back to trunk/ once the changes made are stable). The tags/ directory on the other hand, contains all the released or tagged versions. A tagged version should no longer be modified as that version has already been frozen. It is a convention to structure your SVN repository as such because it makes it easier to identify or to know where to look for specific sources. Maven and Continuum both use this structure in determining the default tag URL when releasing a project. Now, let's import our example projects centrepoint and its parent project, effectivemaven-parent, to our SVN repository. In the sample code for this article, cd to the effectivemaven-parent first then execute the following command: $ svn import . file://localhost/path/to/data/svn/effectivemaven-parent/trunk Do the same with centrepoint. cd to the centrepoint directory and execute: $ svn import . file://localhost/path/to/data/svn/centrepoint/trunk Before importing new projects in a source repository, make sure that there are no unnecessary files (like the Maven generated target/ directory or IDE descriptor files) present. Execute mvn clean to cleanup target/directories or mvn eclipse:clean and mvn idea:clean to remove Eclipse and IntelliJ IDEA descriptor files before adding the project in source control. Installing prerequisites We know that Continuum supports four different types of projects: Maven 1, Maven 2, Ant, and Shell projects. In order for Continuum to build them, of course we need to have the necessary tools installed. Not all of them are required to be installed though, it really depends on the projects you will be adding to and building in Continuum. It does not make sense to have Ant installed if all of your projects are in Maven 2. Maven 1 can be downloaded at http://maven.apache.org/maven-1.x/start/download.html. To install Ant, we can follow the steps specified at the following web page http://ant.apache.org/manual/index.html. Apart from the build tools above, we will also need a mail server. If you do not have a mail server installed in your machine, you can use Apache James (http://james.apache.org). Following steps 1 to 4 in http://wiki.apache.org/james/JamesQuickstart should be enough to get you started. Installing and configuring Continuum Now that we have everything ready, we can proceed with the installation and setup of Continuum. Continuum can be downloaded from http://continuum.apache.org/download.html either as a standalone bundle or as a WAR file (just like Archiva). In our case, we will be using the standalone bundle. To use the WAR file and deploy it to an application server, just follow the steps specified in http://continuum.apache.org/docs/1.3.3/installation/installation.html. The latest version of Continuum is 1.3.3. Just download the bundle and unpack it. I have identified below the directories and configuration files that we need to take note of in Continuum: conf/continuum.xml: This contains Continuum-specific configuration such as the location of the build and release output directories, working copy directory, and the base URL for accessing the webapp. The base URL is used or emails and such, which provides a URL back to the application. conf/jetty.xml : This is a Jetty-specific configuration. This is where the Continuum and Redback databases and mail resources are configured. data/: This is the default location of the Continuum and Redback databases as seen in the jetty.xml file. This is also the default base location of the working directory, build output directory, and release output directory for projects being built in Continuum. logs/: This is the default location of the Jetty and Continuum log files. The location of the log files can be set in the jetty-logging.xml for Jetty and apps/continuum/WEB-INF/classes/log4j.xml for Continuum-specific logs. Once we start up Continuum, we will be seeing different types of log files created in this directory. These are composed of the Continuum rolling log file, audit logs, Jetty logs and wrapper log. We can see in the bin/ directory that there are multiple Java wrappers for starting up Continuum. The following OS platforms are supported: linux-x86-32, linuxx86-64, windows, macosx-universal-32, solaris-sparc-32, solaris-sparc-64 and solaris-x86-32. Before we start Continuum, we must change the port it will run on. Otherwise, we will encounter an Address already in use error during startup. To change the port, edit the configuration below in the conf/jetty.xml file. Change the default value of the jetty.port system property from 8080 to 8082. <!-- START SNIPPET: jetty_port --><Call name="addConnector"> <Arg> <New class="org.mortbay.jetty.nio.SelectChannelConnector"> <Set name="host"><SystemProperty name="jetty.host" /></Set> <Set name="port"><SystemProperty name="jetty.port" default="8082"/></Set> <Set name="maxIdleTime">30000</Set> <Set name="Acceptors">2</Set> <Set name="statsOn">false</Set> <Set name="confidentialPort">8443</Set> <Set name="lowResourcesConnections">5000</Set> <Set name="lowResourcesMaxIdleTime">5000</Set> </New> </Arg></Call> Now let's fire up Continuum. In the bin/ directory, execute ./continuum console if you want to see the logs in the console or ./continuum start to run it in the background. If you are running on Windows, you need to execute continuum.batconsole to execute it in console mode or continuum.bat install then continuum.bat start to run Continuum as a service. I am sure you have noticed during startup that the Continuum standalone uses an embedded Jetty server similar to the one Archiva uses. Did you know that the Archiva and Continuum standalone bundles were created using Maven plugins? These are the appassembler-mavenplugin that was used to generate the Java Service Wrappers including the wrapper configuration, and the maven-assembly-plugin, which was used to package the binaries bundle. Once Continuum has started up successfully, we can access it in the web browser via the URL http://localhost:8082/continuum. You can easily change Continuum's URL either by setting it in the webapp through the General Configuration page or by manually setting it in the <baseUrl> of continuum.xml. One thing to keep in mind when changing the host or the context from the default in Jetty (or in the application server), is to make sure that you change the <baseUrl> in the configuration file as well. Accessing the above URL gives us the Create Admin page (Yes, this is just like Archiva.) For consistency purposes, we will use the same credentials we used in Archiva for the default System Administrator user. Fill in the required fields as follows: Full Name: Administrator Email Address: admin@example.com (this is our official phony admin email address) Password: admin1 After creating the admin user, we will be directed to the General Configuration page, which looks like the following: The Working Directory is where Continuum keeps the local checkouts of the projects it is building, while the Build Output Directory and Release Output Directory are where Continuum stores the respective output of the project builds and the releases done in Continuum. We have already tackled what the Base URL is for, so we will be skipping that. As for the last two fields—Number of Builds in Parallel and Enable Distributed Builds. The contents specified in the fields above will be saved in the continuum.xml file once we hit the Save button. As a general practice, the Working Directory, Build Output Directory and Release Output Directory should be kept separate from the Continuum installation. This is to avoid accidentally deleting these directories when upgrading Continuum. Using Continuum Now, we will see how Continuum builds a project, and how it uses and works with Maven. At a glance Outlined below is a brief step-by-step explanation of how Continuum builds a project: Project build is triggered (either by the user explicitly triggering the build or through the schedule). Continuum adds the build to the build queue and determines the build definition and the build environment to be used for the build. You will understand what it is I am talking about once we get to the middle parts of this article. Continuum checks out a fresh copy or updates the project's working copy (or working directory) from the source repository. A working directory is just a checkout of the project's sources. Once the checkout or update is finished, Continuum builds the project using the build definition from Step 2. It actually just invokes the configured build tools such as Maven or Ant to build the project. When the build is finished, Continuum would collect the results of the build and stores them in the database. Continuum sends out notifications or messages to the project team regarding the result of the build execution—the build is broken, the build is fine, and so on. If you later install Continuum on a server where you do not have access to the file system, you can browse the working directory through the Working Copy tab of the project in the Continuum web application. Now enough with these concepts and let's start exercising our fingers with some CI action. The first thing we need to do is to check out our example application and its parent POM locally. As we did not host our SVN repository to be accessible via HTTP, we will be adding our project from the filesystem. Execute: $ svn co file://localhost/path/to/data/svn/effectivemaven-parent/trunkeffectivemaven-parent$ svn co file://localhost/path/to/data/svn/centrepoint/trunk centrepoint Now we will be adding our example application to Continuum from the filesystem. In order to be able to do this, we must first enable the file:// protocol in apps/continuum/WEB-INF/classes/META-INF/plexus/application.xml. Otherwise, we will encounter a The specified resource isn't a file or the protocol used isn't allowed error message, if we attempt to add it without enabling it first. By default, the file:// protocol for adding projects is disabled in Continuum due to security reasons. Only http, https and ftp are allowed. Let's stop Continuum fi rst. As we are running on console, we will stop it via Ctrl+C. Use ./continuum stop if Continuum is running in the background or as a service.
Read more
  • 0
  • 0
  • 1872
Unlock access to the largest independent learning library in Tech for FREE!
Get unlimited access to 7500+ expert-authored eBooks and video courses covering every tech area you can think of.
Renews at €18.99/month. Cancel anytime
article-image-developing-application-symfony-13-part-1
Packt
10 Nov 2009
5 min read
Save for later

Developing an Application in Symfony 1.3 (Part 1)

Packt
10 Nov 2009
5 min read
The milkshake shop Our application will be designed for a fictitious milkshake shop. The functional requirements for our shop are: Display of the menu, job vacancies, and locations; these details will be updated from the back office Sign up to a newsletter, where users can submit their details Search facility for the menu Secure backend for staff to update the site The site must be responsive Option for the users to view our vacancies in three languages Creating the skeleton folder structure Symfony has many ways in which it can help the developer create applications with less efforts—one way is by using the Symfony tasks available on the Command Line Interface (CLI). These Symfony tasks do the following: Generate the folder structure for your project, modules, applications, and tasks Clear the generated cache and rotate log files Create controllers to enable and disable an application and set permissions Interact with the ORM layer to build models and forms Interact with SQL to insert data into the database Generate initial tests and execute them Search for text in your templates that need i18N dictionaries and create them If you'd like to see all the tasks that are available with Symfony, just type the following on your command line:symfony Let's begin with using one of the Symfony tasks to generate the file structure for our project. Our project will reside in the milkshake folder. In your terminal window, change your folder path to the path that will hold your project and create this milkshake folder. My folder will reside in workspace folder. Therefore, I would type this: $mkdir ~/workspace/milkshake && cd ~/workspace/milkshake Now that I have the project folder and have changed the path to within the milkshake folder, let's use a Symfony task to generate the project file structure. In your terminal window, type the following: $/home/timmy/workspace/milkshake>symfony generate:project -orm=Propel milkshake We can generate our project we can also specify which ORM we would like to use. Our application is going to use the Propel ORM, but you can also opt for Doctrine ORM. By default, Doctrine ORM is enabled. After pressing the Enter key, the task goes into action. Now you will see output like the one in the following screenshot on your terminal window. This screenshot shows the folder structure being created: Let's have a look at the top-level folders that have been created in our project folder: Folders Description apps This folder contains our frontend application and any other applications that we will create batch If there are any batch scripts, they are placed here cache This folder is the location for cached files and/or scaffolded modules config This folder holds all the main configuration files and the database schema definitions data This folder holds data files such as test or fixture data doc All our documentation should be stored in this folder; this includes briefs, PhpDoc for API, Entity Relationship Diagrams, and so on lib Our models and classes are located in this folder so that all applications can access them log This folder holds the development controllers log files and our Apache log plugins All installed plugins are located in this folder test All unit and functional tests should be placed in this folder; Symfony will create and place stubs in this folder when we create our modules web This is the web root folder that contains all web resources such as images, CSS, JavaScript, and so on There are three folders that must be writable by the web server. These are the cache, log, and web/uploads/. If these are not writable to your web server, then the server itself will either produce warnings at startup or fail to start. The files permissions are usually set during the generation process, but sometimes this can fail. You can use the following task to set the permissions:$/home/timmy/workspace/milkshake>symfony project:permissions With the initial project skeleton built, next we must create an application. Symfony defines an application as operations that are grouped logically and that can run independent of one another. For example, many sites have a frontend and back office/admin area. The admin area is where a user logs in and updates areas on the frontend. Of course, our application will also provide this functionality. We will call these areas frontend and backend. Our first application is going to be the frontend. Again, let's use a Symfony task to generate the frontend application folder structure. Enter the following task in your terminal window: $/home/timmy/workspace/milkshake>symfony generate:app frontend Again, after executing this task, you will see the following in your terminal window: Let's have a look at the top-level folders that have just been created in the apps folder: Directory Description config This folder will have all configuration files for our application i18N This folder will contain all Internationalization files lib This folder will hold all application-specific classes modules All our modules will be built in this folder templates The default layout template is created in this folder, and any other global templates that will be created will also be placed in this folder These steps will generate our project and frontend application folder structure, and we can now view our project in a web browser. Open your web browser and go to http://milkshake/frontend_dev.php and you will see the following default Symfony page: Now we can see the default project page along with the instructions on what to do next. The frontend_dev.php file is our index file while developing the frontend application, and adheres to the naming convention of <application>_<environment>.php. This controller is the development controller and is rather helpful while developing. Before we continue though, I urge you to also have a look at the web debug toolbar.
Read more
  • 0
  • 0
  • 3081

article-image-search-engine-optimization-using-sitemaps-drupal-6
Packt
10 Nov 2009
8 min read
Save for later

Search Engine Optimization using Sitemaps in Drupal 6

Packt
10 Nov 2009
8 min read
Let's get started. As smart as the Google spider is, it's possible for them to miss pages on your site. Maybe you've got an orphaned page that isn't in your navigation anymore. Or, perhaps you have moved a link to a piece of content so that it's not easily accessible. It's also possible that your site is so big that Google just can't crawl it all without completely pulling all your server's resources—not pretty! The solution is a sitemap. In the early 2000s, Google started supporting XML sitemaps. Soon after Yahoo came out with their own standard and other search engines started to follow suit. Fortunately, in 2006, Google, Yahoo, Microsoft, and a handful of smaller players all got together and decided to support the same sitemap specification. That made it much easier for site owners to make sure every page of their web site is crawled and added to the search engine index. They published their specification at http://sitemaps.org. Shortly thereafter, the Drupal community stepped up and created a module called (surprise!) the XML sitemap module. This module automatically generates an XML sitemap containing every node and taxonomy on your Drupal site. Actually, it was written by Matthew Loar as part of the Google Summer of Code. The Drupal 6 version of the module was developed by Kiam LaLuno. Finally, in mid-2009, Dave Reid began working on a version 2.0 of the module to address performance, scalability, and reliability issues. Thanks, guys! According to www.sitemaps.org: Sitemaps are an easy way for Webmasters to inform search engines about pages on their sites that are available for crawling. In its simplest form, a Sitemap is an XML file that lists URLs for a site along with additional metadata about each URL (when it was last updated, how often it usually changes, and how important it is, relative to other URLs in the site) so that search engines can more intelligently crawl the site. Web crawlers usually discover pages from links within the site and from other sites. Sitemaps supplement this data to allow crawlers that support Sitemaps to pick up all URLs in the Sitemap and learn about those URLs using the associated metadata. Using a sitemap does not guarantee that every page will be included in the search engines. Rather, it helps the search engine crawlers find more of your pages. In my experience, submitting an XML Sitemap to Google will greatly increase the number of pages when you do a site: search. The keyword site: searches show you how many pages of your site are included in the search engine index, as shown in the following screenshot: Setting up the XML Sitemap module The XML Sitemap module creates a sitemap that conforms to the sitemap.org specification. Which XML Sitemap module should you use? There are two versions of the XML Sitemap module for Drupal 6. The 1.x version is, as of this writing, considered the stable release and should be used for production sites. However, if you have a site with more than about 2000 nodes, you should probably consider using the 2.x version. From www.drupal.org: 'The 6.x-2.x branch is a complete refactoring with considerations for performance, scalability, and reliability. Once the 6.x-2.x branch is tested and upgradeable, the 6.x-1.x branch will no longer be supported'. What this means is that in the next few months (quite possibly by the time you're reading this) everyone should be using the 2.x version of this module. That's the beauty of open source software—there are always improvements coming that make your Drupal site better Search Engine Optimized. Carry out the following steps to set up the XML Sitemap module: Download the XML Sitemap module and install it just like a normal Drupal module.  When you go to turn on the module, you'll be presented with a list that looks similar to the following screenshot: Now that you have the XML sitemap module properly installed and configured, you can start defining the priority of the content on your site—by default, the priority is .5. However, there are times when you may want Google to visit some content more often and other times when you may not want your content in the sitemap at all (like the comment or contact us submission forms). Each node now has an XML sitemap section that looks like the following screenshot: Before you turn on any included modules, consider what pieces of content on your site you want to show up in the search engines and only turn on the modules you need. The XML sitemap module is required. Turn it on. XML sitemap custom allows you to add your own customized links to the sitemap. Turn it on. XML sitemap engines will automatically submit your sitemap to the search engines each time it changes. This is not necessary and there are better ways to submit your sitemap. However, it does a nice job of helping you verify your site with each search engine. Turn it on. XML sitemap menu adds your menu items to the sitemap. This is probably a good idea. Turn it on. XML sitemap node adds all your nodes. That's usually the bulk of your content so this is a must-have. Turn it on. XML sitemap taxonomy adds all your taxonomy term pages to the sitemap. Generally a good idea but some might not want this listed. Term pages are good category pages so I recommend it. Turn it on. Don't forget to click Save configuration. Go to http://www.yourDrupalsite.com/admin/settings/xmlsitemap or go to your admin screen and click on Administer | Site Configuration | XML sitemap link. You'll be able to see the XML sitemap, as shown in the following screenshot: Click on Settings and you'll see a few options, as shown in the following screenshot: Minimum sitemap lifetime: It determines that minimum amount of time that the module will wait before renewing the sitemap. Use this feature if you have an enormous sitemap that is taking too many server resources. Most sites should leave this set on No minimum. Include a stylesheet in the: The sitemaps will generate a simple css file to include with the sitemap that is generated. It's not necessary for the search engines but very helpful for troubleshooting or if any humans are going to view the sitemap. Leave it checked. Generate sitemaps for the following languages: In the future, this option will allow you to actually specify sitemaps for different languages. This is very important for international sites who want to show up in localized search engines. For now, English is the only option and should remain checked. Click the Advanced settings drop-down and you'll see several additional options. Number of links in each sitemap page allows you to specify how many links to pages on your web site will be in each sitemap. Leave it on Automatic unless you are having trouble with the search engines accepting the sitemap. Maximum number of sitemap links to process at once sets the number of additional links that the module will add to your sitemap each time the cron runs. This highlights one of the biggest differences between the new XML sitemap and the old one. The new sitemap only processes new nodes and updates the existing sitemap instead of reprocessing every time the sitemap is accessed. Leave this setting alone unless you notice that cron is timing out. Sitemap cache directory allows you to set where the sitemap data will be stored. This is data that is not shown to the search engines or users; it's only used by the module. Base URL is the base URL of your site and generally should be left as it is. Click on the Front page drop-down and set these options: Front page priority: 1.0 is the highest setting you can give a page in the XML sitemap. On most web sites, the front page is the single most important part of your site so, this setting should probably be left at 1.0. Front page change frequency: Tells the search engines how often they should revisit your front page. Adjust this setting to reflect how often the front page of your site changes. What is priority and how does it work? Priority is an often-misunderstood part of a sitemap. For instance, the priority is only used to compare pages of your own site and you cannot increase your ranking in the Search Engine Results Page (SERPS) by increasing the priority of your pages. However, it does help let the search engines know which pages of your site you feel are more important. They could use this information to select between two different pages on your site when deciding which page to show to a search engine user.
Read more
  • 0
  • 0
  • 1775

article-image-short-tour-through-nav-2009-part-1
Packt
10 Nov 2009
6 min read
Save for later

A Short Tour through NAV 2009: Part 1

Packt
10 Nov 2009
6 min read
A product as complex and flexible as NAV can be considered from several points of view. One can study the NAV application software package as a set of application functions designed to help a business manage information about operations and finances. One can also look at NAV as a stack of building blocks from which to extend or build applications—and the tools with which to do the construction. In NAV 2009, which has two quite different user interface options available, one must consider how the user interface affects both the application design and the presentation to the user. This requirement overlaps both the application viewpoint and the construction viewpoint. You should know the different object types that make up a NAV system and the purposes of each. You should also have at least a basic idea of the tools that are available to you, in order to enhance (small changes) or extend (big changes) an NAV system. In the case of NAV, the Integrated Development Environment (IDE) includes essentially all of the tools needed for NAV application development. Prior versions of NAV were two-tier systems. One of the tiers was the database server, the other tier was the client. As the traditional two-tier NAV Client (now referred to as the Classic Client) is still an integral part of the system, we will cover the aspects of where it must be used for development and support. All development and much of the system administration uses the Classic Client. So, even though our focus is on developing for the Role Tailored Client (aka "the RTC"), many of the images in this article will be of Classic Client displays. In brief, the RTC is for users, and as a developer, you will generally use the Classic Client for your work. NAV 2009: An ERP system If you look at NAV 2009 from the point of view of a firm using NAV to help run its business, you will see it as an integrated set of business applications software. Microsoft Dynamics NAV is generally characterized as an ERP System. ERP stands for Enterprise Resource Planning. An ERP system is a set of integrated application software components designed to track and coordinate a wide variety of business activities, especially those involving products, orders, production and finances. An ERP system will typically include the following: Basic accounting functions (for example, general ledger, accounts payable, accounts receivable) Order processing and inventory (for example, sales orders, purchase orders, shipping, inventory, receiving) Relationship management (for example, vendors, customers, prospects, employees, contractors, and so on) Planning (for example MRP, sales forecasting, production forecasting) Other critical business areas (for example, manufacturing, warehouse management, fixed assets) The integration of an ERP system is supported by a common database, by an "enter once, use everywhere" data philosophy, by a modular software design, and with data extraction and analysis tools. The following image is a view of an ERP system from the highest level: The design of Microsoft Dynamics NAV addresses all the points in the above description and more. The NAV ERP system includes integrated modules covering the breadth of business functions for a wide range of business types. These modules share a common database and, where appropriate, share common data. In the NAV system, there is a considerable overlap of components across application areas, with individual functions showing up in multiple different "modules". For example, in NAV, Inventory is identified as part of Financial management, but it is also, obviously, an integral part of Manufacturing, Supply Chain, and others. The particular grouping of individual functions into modules that follows is based on Microsoft marketing materials. Some of these assignments are a bit arbitrary. What's important is for you to obtain a reasonable understanding of the overall set of application components that make up the NAV ERP system. In several of the following groupings, menu screenshots are included as examples. These are from the Role Tailored Client Departments menu screen. Financial Management Financial Management is the foundation of any ERP system. No matter what the business is, the money must be kept flowing, and the flow of money must be tracked. The tools which help to manage the capital resources of the business are included in NAV's Financial Management module. These include all or part of the following application functions: General Ledger—managing the overall finances of the firm Accounts receivable—tracking the incoming revenue Accounts payable—tracking the outgoing funds Analytical accounting—analyzing the various flows of funds Cash management and banking—managing the inventory of money Inventory and fixed assets—managing the inventories of goods and equipment Multi-Currency and Multi-Language—supporting international business activities Manufacturing NAV Manufacturing is general purpose enough to be appropriate for Make to Stock (MTS), Make to Order (MTO), and variations such as Assemble to Order, and so on. While off-the-shelf NAV is not particularly suitable for most process manufacturing and high-volume assembly line operations, there are third party add-on and add-in enhancements available for these. As with most of the NAV application functions, manufacturing can be installed in parts or as a whole, and can be used in a simplistic fashion or in a more sophisticated manner. NAV Manufacturing includes the following functions Product design (BOMs and Routings)—managing the structure of product components and the flow of manufacturing processes Capacity and supply requirements planning—tracking the intangible and tangible manufacturing resources Production scheduling (infinite and finite),execution, and tracking—tracking the planned use manufacturing resources, both on an unconstrained and constrained basis Supply Chain Management (SCM) Obviously, some of the functions categorized as part of NAV Supply Chain Management (for example sales, purchasing, and so on) are actively used in almost every NAV implementation. As a whole, these constitute the base components of a system appropriate for a distribution operation. The Supply Chain applications in NAV include parts of the following applications: Sales order processing and pricing—supporting the heart of every business—entering, pricing, and processing sales orders Purchasing (including Requisitions)—planning, entering, pricing, and processing purchase orders Inventory management—managing inventories of goods and materials Warehouse management including receiving and shipping—managing the receipt, storage, retrieval, and shipment of material and goods in warehouses
Read more
  • 0
  • 0
  • 1418

article-image-keyword-research-search-engine-optimization-drupal-6
Packt
10 Nov 2009
11 min read
Save for later

Keyword Research for Search Engine Optimization in Drupal 6

Packt
10 Nov 2009
11 min read
SEO is necessary—you've got to do it if you want to rank well for keywords. Simple in concept, keywords are actually very complicated things. They bring order to chaos, define markets, and reveal intent. Keyword data simultaneously tells you how many people are looking for your product or service and what those people will do once they find you. The results of a keyword search can tell you who the top people are in an industry and inform you of upcoming trends in the market. Keywords are the most visible focal point of free market competition between business interests. Search engine optimization is a popularity contest for keywords and this is a popularity contest you want to win. The most critical part of an SEO project is finding the right keywords. You will spend months working on your web site, getting links, and telling the world that your site is the authority on that keyword. It's critical that when you finally arrive, your customers are there to embrace you. If you pick the wrong keywords, you'll spend months working only to find that there is nobody who wants to buy your product. Ouch! An extra few hours researching your keywords in the beginning will help you avoid this fate. What a keyword is Keywords are many things to many people. For the purpose of this SEO campaign, there are really only two things about keywords that we need to understand to get the job done. Keywords aggregate searchers into organized groups and a keyword defines a market. Keywords are single words that a search engine user types into the search box to try to find what they're looking for. Key phrases are the same as keywords except for the fact that they consist of two or more words. For the sake of simplicity, throughout this article let's use keywords to mean both, keywords and key phrases. Keywords aggregate searchers into organized groups Millions of random people visit Google every day. When they arrive, they are amorphous—a huddled mass yearning for enlightenment with nothing more than a blank Google search form to guide them. As each person types keywords into Google and clicks the Search button, this random mass of people becomes extraordinarily organized. Each keyword identifies exactly what that person is looking for and allows Google to show them results that would satisfy their query. Much like a labor union, the more searchers there are looking for a particular phrase, the more clout they have with the businesses who want to sell to them. However, instead of more pay and better health benefits, you get better search results. If there are a thousand people per month looking for keyword A and a hundred people per month looking for keyword B, then chances are good that there are more competitors focused on keyword A. More competition means better optimization is required to show up at the top. Better optimization requires more content, closer attention to meeting the needs of the group, and more interesting web sites. A keyword defines a market This organization of searchers is what gives Google such power. In a very real way, Google creates billions of tiny markets every day. There is a buyer (the searcher) looking for a product, the seller (the web site owners) selling what they've got, and the middleman (Google) organizing everything and facilitating the transaction. The transaction takes place when the searcher clicks on the result of a keyword search and is whisked off to the seller's web site. However, it doesn't always go smoothly. In fact, very high percentages of the time the searcher doesn't find what they're looking for so they hit the back button and try again. They may try a different result on the same page or type in a different keyword and do the entire search again. Each time you have an opportunity to convince them that yours is the right site with the best information and most promising solution to their questions. It is in your best interest to provide a web site that quickly engages the searchers, pulls them in, and keeps the dialogue going. Why keyword research is important As a Drupal site owner, you have the opportunity to position yourself as the best site available for the keywords people are searching for. Know thy customerThere are hundreds of good marketing books out there to help you better understand your audience. All that good information applies to SEO as well. The better you know your audience, the better you can guess what keywords they are typing in Google to find companies like yours. You're an expert in your field, so of course you know many of the keywords that people use to find your products and services. But, are you sure you know them all? A few years ago Tom, a friend of mine, hired me to do SEO for his high-end landscaping firm. His company designs and installs yards, trees, retaining walls, and so on, outside million dollar homes in the hill country near Austin, Texas. We sat down in an early morning meeting and he said, "Ben, the right keyword is landscaping. I know it so there's no reason to do all this research. Don't waste your time and my money. Just do landscaping and that's that". Being the thorough person that I am, I did the keyword research anyway. Guess what I found? The number one phrase in his business was landscaping. However, a very close second was landscaper. And, while landscaping had dozens of competitors—some of them were very well entrenched—there were only a handful of competitors showing up for landscaper. The next day, I called Tom and told him what I found. "You know what?" he said, "Now that you mention it, many of our customers do refer to us as landscapers—'I need a landscaper. Call a landscaper' ". So, we started his campaign targeting the keyword landscaper. Because there was so little competition, he ranked in the top five in a matter of weeks and was number one in Google within two months. He was dominating half the search traffic within two months! The leads were rolling in so we switched to the keyword landscaping. It took longer—about three months—for him to break into the top ten. By that time, he had so many inquiries, he hardly even noticed. The lesson here is three-fold: You may know some of the keywords, however, that doesn't mean you know them all. Just because you think of yourself in one particular way doesn't mean your customers do. By taking the time to do keyword research, you will reveal opportunities in your market that you didn't know existed. What your keyword goal is Before you start looking at keywords, you need to fix your goal firmly in your mind. There are basically two major reasons to do SEO. Goal 1: Brand awareness This may come as a surprise but there are people out there who don't know that you exist. SEO is a powerful and inexpensive way to get your name out there and build some credibility with your target customers. There are three major types of brand awareness: Company brand awareness Company brand awareness works on getting the name of your company into the market. If you want to build credibility for Big Computers Unlimited as a whole, then you probably want a campaign focused on getting your company listed where other top producers of PCs are listed. PC, computer, or fast computer all might be good terms. Product brand awareness Product brand awareness focuses on building general market knowledge of one product or line of products that your company produces. If you work for Big Computers Unlimited and you want to sell more Intergalactic Gamer brand computers at retail stores throughout the country, then you probably want to build a campaign around keywords like Gaming PC or even high-end PC. Credibility A 2004 survey by iProspect found that two out of three search engine users believed that the highest search results in Google were the top brands for their industry; there is little reason to believe this perception has changed. That means that just by being at the top of Google will gain you a certain level of trust among search engine users. If Big Computers Unlimited can rank in the top three for Gaming PCs, they'll develop a lot of creed among gamers. Goal 2: Conversions Conversions are a fancy way of saying that the visitor did what you wanted them to do. There are three typical types of conversions: Transactional A transaction is just what it sounds like. Someone puts in a credit card and buys your product. This is typical of most product-focused web sites but isn't limited to this narrow category. Your web site may sell registrations to online training, subscriptions to magazines, or even credit monitoring. The bottom line is that it can be purchased on the site. You need to focus your keyword research on terms that will bring buyers who are ready to purchase right now. Words like buy, price, merchant, store, and shop indicate a desire for immediate purchase. Give them the transactional information they need like price, color choices, size, quantity discounts, return policy, and delivery options. With this information and a great checkout experience you'll have them buying from you in no time. UbercartUbercart is simply the best shopping cart solution for Drupal. If you're a transactional web site and you need an e-commerce solution, start here: http://www.ubercart.org/. Lead Generation If you're in an industry with a long sales cycle like real estate, legal services, or medical, then you're probably interested in generating leads rather than online transactions. If you sell a service or product that requires direct contact with your customer, like consulting or personal training, then you probably want leads too. Lead generation means that instead of buying from your web site, you're interested in someone expressing an interest in doing business with you so that you can follow up with them later. You let them express interest by filling out a contact form, emailing you, or even picking up the phone and calling you. You need to focus your keyword research on terms that will bring people who are perhaps a little earlier in the buying process. Words like review, compare, best, information, and generic names of your product indicate a user is researching but not quite ready to buy. You'll need to provide a lot of information on your web site to inform them and shape their thinking about your product. Page impression (or ad impression) Some web sites make money when visitors view an ad. To these sites, a conversion may simply be someone clicking on one or more pages so that they'll see one more ads. You need to focus your keyword research on terms that will bring people seeking information or news to your web site. Keyword research tools There are many tools to help you find the right keywords. It's not important that you use them all but you should try a few of them just so you can see what's out there. Here are my favorites in order of preference: Your own web site The most important keyword research tool at your disposal is your own web site, http://www.yourDrupalsite.com/. If you already have some traffic, chances are that they're coming from somewhere. With analytics installed, you should be able to find out what they're searching on with just a few clicks. If you have Google Analytics installed then you can easily see this data by logging in to its admin section and then going to Traffic Sources | Search Engines | Google. This information can be invaluable if you cross-reference it with your current positions in the search engines. Say, for example, that you're getting 100 searchers a month on a term that you're on page 2 of Google. That's a good indicator that people are searching hard to find a company like yours and that may be a very good term to focus on with your search engine campaign. If you're getting that much traffic on page 2, imagine if you were in the top three on page 1. Drupal has a built-in search engine—another great tool to see what the people are searching for, after they've already visited your site. There's an insanely useful module for that, called Top Searches (http://drupal.org/project/top_searches) that does a better job that Drupal's built-in list. This module was developed by the founder of Linnovate, Zohar Stolar. Thanks, Zohar!
Read more
  • 0
  • 0
  • 1499
article-image-developing-application-symfony-13-part-2
Packt
10 Nov 2009
7 min read
Save for later

Developing an Application in Symfony 1.3 (Part 2)

Packt
10 Nov 2009
7 min read
Building the database The last step is to create the database and then create all of the tables. I have created my database called milkshake on the CLI using the following command: $/home/timmy/workspace/milkshake>mysqladmin create milkshake -u root -p Now that we have created the database, we need to generate the SQL that will create our tables. Again, we are going to use a Symfony task for this. Just like creating the ORM layer, the task will build the SQL based on the schema.xml file. From the CLI, execute the following task: $/home/timmy/workspace/milkshake>symfony propel:build-sql This has now generated a SQL file that contains all of the SQL statements needed to build the tables in our database. This file is located in the data/sql folder within the project folder. Looking at the generated lib.model.schema.sql file in this folder, you can view the SQL. Next, we need to insert the SQL into the database. Again using a Symfony task, execute the following on the CLI: $/home/timmy/workspace/milkshake>symfony propel:insert-sql During the execution of the task, you will be prompted to enter a y or N as to whether you want to delete the existing data. As this command will delete your existing tables and then create new tables, enter y. During development, the confirmation can become tiring. To get around this you can append the no-confirmation switch to the end as shown here: >symfony propel:insert-sql --no-confirmation Afterwards, check in your database and you should see all of the tables created as shown in the following screenshot: I have showed you how to execute each of the tasks in order to build everything. But there is a simpler way to do this, and that is with yet another Symfony task which executes all of the above tasks: $/home/timmy/workspace/milkshake>symfony propel:build-all or $/home/timmy/workspace/milkshake>symfony propel:build-all --no-confirmation Our application is now all set up with a database and the ORM layer configured. Next, we can start on the application logic and produce a wireframe. Creating the application modules In Symfony, all requests are initially handled by a front controller before being passed to an action. The actions then implement the application logic before returning the presentation template that will be rendered. Our application will initially contain four areas—home, location, menu, and vacancies. These areas will essentially form modules within our frontend application. A module is similar to an application, which is the place to group all application logic and is self contained. Let's now create the modules on the CLI by executing the following tasks: $/home/timmy/workspace/milkshake>symfony generate:module frontend home$/home/timmy/workspace/milkshake>symfony generate:module frontend location$/home/timmy/workspace/milkshake>symfony generate:module frontend menu$/home/timmy/workspace/milkshake>symfony generate:module frontend vacancies Executing these tasks will create all of the modules' folder structures along with default actions, templates, and tests in our frontend application. You will see the following screenshot when running the first task: Let's examine the folder structure for a module: Folder Description actions This folder contains the actions class and components class for a module templates All modules templates are stored in this folder Now browse to http://milkshake/frontend_dev.php/menu and you will see Symfony's default page for our menu module. Notice that this page also provides useful information on what to do next. This information, of course, is to render our template rather than have Symfony forward the request. Handling the routing We have just tested our menu module and Symfony was able to handle this request without us having to set anything. This is because the URL was interpreted as http://milkshake/module/action/:params. If the action is missing, Symfony will automatically append index and execute the index action if one exists in the module. Looking at the URL for our menu module, we can use either http://milkshake/frontend_dev.php/menu or http://milkshake/frontend_dev.php/menu/index for the moment. Also, if you want to pass variables from the URL, then we can just add them to the end of the URL. For example, if we wanted to also pass page=1 to the menu module, the URL would be http://milkshake/frontend_dev.php/menu/index/page/1. The problem here is that we must also specify the name of the action, which doesn't leave much room for customizing a URL. Mapping the URL to the application logic is called routing. In the earlier example, we browsed to http://milkshake/frontend_dev.php/menu and Symfony was able to route that to our menu module without us having to configure anything. First, let's take a look at the routing file located at apps/frontend/config/routing.yml. # default ruleshomepage: URL: / param: { module: default, action: index }default_index: URL: /:module param: { action: index }default: URL: /:module/:action/* This is the default routing file that was generated for us. Using the home page routing rules as an example, the route is broken down into three parts: A unique label: homepage A URL pattern: URL: / An array of request parameters: param: { module: menu, action: index } We refer to each one of the rules within the routing file using a unique label. A URL pattern is what Symfony uses to map the URL to a rule, and the array of parameters is what maps the request to the module and the action. By using a routing file, Symfony caters for complicated URLs, which can restrict parameter types, request methods, and associate parameters to our Propel ORM layer. In fact, Symfony includes an entire framework that handles the routing for us. The application logic As we have seen, Symfony routes all requests to an action within a module. So let's open the actions class for our menu module, which is located at apps/frontend/modules/menu/actions/actions.class.php. class menuActions extends sfActions{ /** * Executes index action * * @param sfRequest $request A request object */ public function executeIndex(sfWebRequest $request) { $this->forward('default', 'module'); }} This menuActions class contains all of the menu actions and as you can see, it extends the sfActions base class. This class was generated for us along with a default 'index' action (method). The default index action simply forwards the request to Symfony's default module, which in turn generates the default page that we were presented with. All of the actions follow the same naming convention, that is, the action name must begin with the word execute followed by the action name starting with a capital letter. Also, the request object is passed to the action, which contains all of the parameters that are in the request. Let's begin by modifying the default behavior of the menu module to display our own template. Here we need the application logic to return the template name that needs to be rendered. To do this, we simply replace the call to the forward function with a return statement that has the template name: public function executeIndex(sfWebRequest $request) { return sfView::SUCCESS; } A default index template was also generated for us in the templates folder, that is, apps/frontend/modules/menu/templates/indexSuccess.php. Returning the sfView::SUCCESS constant will render this template for us. The template rendered will depend on the returned string from the action. All templates must also follow the naming convention of actionNameReturnString.php. Therefore, our action called index returns the sfView constant SUCCESS, meaning that the indexSuccess.php template needs to be present within the templates folder for our menu module. We can return other strings, such as these: return sfView::ERROR: Looks for the indexError.php template return myTemplate: Looks for the indexmyTemplate.php template return sfView::NONE: Will not return a template and, therefore, bypass the view layer; this could be used as an example for an AJAX request However, just removing the $this->forward('default', 'module') function will also return indexSuccess.php by default. It is worth adding the return value for ease in reading. Now that we have rendered the menu template, go ahead and do the same for the home, locations, and vacancies modules.
Read more
  • 0
  • 0
  • 1440

article-image-creating-and-using-templates-cacti-08
Packt
10 Nov 2009
3 min read
Save for later

Creating and Using Templates with Cacti 0.8

Packt
10 Nov 2009
3 min read
For example, let's say we have a network of four Linux servers, two Unix servers, and one Cisco router. Here, if we use a template, we will need to make only three different templates: one for the Linux servers, one for the Unix servers, and one for the Cisco router. You may ask why we have to make a template for the Cisco router? We will make it so that we can use it later. Cacti templates can be imported and exported via the Console under Import/Export. You can only import templates that have been created on a system that is at the same, or an earlier, version of Cacti. At the end of this article, there is a list of third-party templates that can be imported. Types of Cacti templates Cacti templates are broken into two areas: Graph templates Host templates In the above figure, under the Templates section, you will see the three types of templates that come with the Cacti basic installation. If you click on one of those links, you will get the complete list of templates for that type. Graph templates Graphs are used to visualize the data you have collected. A graph template provides a skeleton for an actual graph. When you have more then one system/device, a graph template will save you lots of time and also reduce the possibility of error. Any parameters defined within a graph template are copied to all the graphs that are created using this template. Creating a graph template Now, we are going to create a new graph template. Under the Templates heading, click on Graph Templates. A list of the already available graph templates will be shown. Click on the Add link in the top right corner. You will get a screen like this: Here, you have to give the values that will be used in future to create the graph. If you have checked Use Per-Graph Value (Ignore this Value), then every time while using this graph template to create a graph, you have to give an input for this option. It's always best to enable this option for title field. After filling all these fields, click on the Create button. The graph template will be created. Now, we need to add a Graph Template Item and Graph Item Inputs to complete this graph template. Graph Template Item Graph template items are the various items that will be shown on the graph. To add a graph template item, click on Add on the right side of the Graph Template Items box. You will get this page. The following are the fields that can be filled in: When creating a graph item, you must always start with an AREA item before using STACK; otherwise, your graph will not render. Graph Item Inputs The second box is Graph Item Inputs. Graph item inputs are the input source through which the data will be collected. To add a new graph item input, click on the Add link on the right side of the Graph Item Inputs box. Below are the various fields that have to be filled out for a graph input item: After completing all these fields, click on the Create button. Do this again to add more graph item inputs to this graph template.
Read more
  • 0
  • 0
  • 8822

article-image-apache-continuum-ensuring-health-your-source-code-part-2
Packt
10 Nov 2009
10 min read
Save for later

Apache Continuum: Ensuring the Health of your Source Code (Part 2)

Packt
10 Nov 2009
10 min read
The build definition As we have seen in the Continuum build life cycle overview earlier, Continuum uses build definitions for building the projects. We can think of build definitions as instructions for Continuum on how to build the project. There are two levels of build definition: project group level and project level. Project group build definition Let's go back to the Project Group Summary page of our centrepoint application and click on the Build Definitions tab. Continuum configures a default build definition for each project group created. The content of the build definition depends on the type of the project. For Maven 2 projects, goals such as clean install and clean deploy need to be defined while Ant and shell projects do not require this. Instead, they configure their respective command-line goals. As we can see from the Project Group Build Definition of our Centrepoint project group below, the goals to be executed for the build can be configured as well as the schedule of its execution. The default Project Group Build Definition has clean install as its goals. Having install as a goal means that the project's artifacts will only be available locally (in the local Maven repository) to where our Continuum instance is running. Consumers of the project (for example, the project's Quality Assurance team) will not necessarily have permission to access the machine itself and it is out of the question to obtain the latest build from the local repository. Instead, the right way to do is to set up a deployment repository for snapshot artifacts, configure Continuum to build the project, and deploy the built artifacts to the snapshot repository, so that there is a new build each day that can be consumed by the QA team for testing. ...<distributionManagement> <repository> <id>releases</id> <name>Archiva Managed Releases Repository</name> <url>http://localhost:8081/archiva/repository/releases</url> <layout>default</layout> </repository> <snapshotRepository> <id>snapshots</id> <name>Archiva Managed Snapshots Repository</name> <url>http://localhost:8081/archiva/repository/snapshots</url> <uniqueVersion>true</uniqueVersion> <layout>default</layout> </snapshotRepository></distributionManagement>... We don't need to configure anything else in our settings.xml and security-settings.xml. Now let's create a new build definition for our Centrepoint project group. Click the Add button on the group build definitions page, then fill in the Build Definition as follows: The POM filename is the actual filename of our project's pom.xml that will be used for the build. The Goals are the goals we want to execute when using this build definition. As we want our artifacts to be available to the snapshots repository, we will set clean deploy as our goals. The Arguments specify the additional arguments that will be used when building the project. In our case, these are Maven 2 arguments, as our project is a Maven 2 project. If you had wanted to do a fresh checkout instead of just updating from the source repository during the build, you would tick the Build Fresh checkbox. One behavior of Continuum to take note of is that if there were no changes to the SCM in the last build; Continuum would no longer build the project. This sometimes causes people to panic and think their CI is broken, but it is actually not. When you think about it, it makes sense that this is the default behavior. There were no changes in the code, so why build it? To countermand this behavior, you would tick the Always Build checkbox in the build definition. This would tell Continuum to always build the project whether there were any changes in the SCM or not. The Is it default? field signifies that the build definition is the default one that will be used when the project build is explicitly triggered (forced build) from the Project Group Summary and Project Summary pages. This field can only be edited if there are multiple build definitions defined for the project. We will discuss schedules next, but for now we will just use the DEFAULT_SCHEDULE. The same goes for Build Environment. We will cover this at the end of this article. For now, we will leave as it is. As for the last two fields, they simply specify the type of project the build definition is for and the description of the build definition respectively. Click Save and our build definition is created. Now for each project group, we can define multiple build definitions and attach it to a schedule. A schedule is just a plain old schedule of when a build will be triggered or executed. The diagram below, which Wendy Smoak created for Continuum effectively shows how the project group, build definitions, schedules, build queues, and notifiers (which we will discuss later on) work together. Going back to the schedules, a default schedule running every hour is already pre-defined in Continuum and that is where the default build definition is attached. We will create a schedule for our project's nightly builds where we will attach the build definition that we created so that there is a fresh snapshot each day which the QA team can test for all the changes or fixes in the code from the previous day. From the Schedules tab on the left you can add a new schedule. Fill in the form as follows: The important field to take note of in the Schedule is the Cron Expression. A Cron Expression is a representation of a time or times at certain intervals. This is used typically to fire or trigger a schedule to do or execute something. From the Cron Expression we have specified above, we are configuring our schedule to build each night at 12 midnight. The other field to take note of is the Build Queue. This simply defines where the project(s) attached to this schedule will be queued. If no build queue were set, Continuum would use the DEFAULT_BUILD_QUEUE. Save the schedule that we created. Go back to Centrepoint's Build Definitions tab, edit the Project Group Build Definition we created earlier and attach it to our NIGHTLY_BUILDS schedule. When setting a schedule, sometimes an hour is too long to wait. If a problem occurs it may be drowned out by other changes, or the developer may have switched context. Builds should be kept short and scheduled frequently. Long-running tasks, such as performance and functional tests, and nightly builds can be run on separate schedules. To demonstrate how build definitions are used, click the build icon (third column from the right) of our snapshot deployment build definition. After the projects in the group have finished building, go back to the Project Group Summary page and click on the build number link (fifth column from the left) of one of the projects. Clicking on the build number leads us to the build result of that specific build including the build definition used and the build output. We will cover build results in the succeeding sections. You can also check the snapshots repository at http://localhost:8081/archiva/repository/snapshots to see that the projects were deployed. Project build definition A project level build definition is essentially the same as a project group build definition—it could be tied to a schedule and everything. The only difference is that it's on a per project level. Therefore, when only that project was triggered to be built, continuum would use the default project level build definition. The notifiers We mentioned earlier in the article that one of the benefits of using a CI server is that when the build breaks, the project members would immediately know of it. This is where notifiers come in. Notifiers are a configuration in the CI, which send out notifications to the project members regarding the status of the build. Different types of notifiers In Continuum, there are five types of notifiers that can be configured. These are the: Mail notifier: The mail notifier sends out an email to a specific address regarding the status of the build. For the mail notifier to work, it's good to keep in mind to set up a mail server first. Remember, we did this whilst installing prerequisites at the beginning of the article. IRC notifier: This notifier sends out a message to a specific IRC channel alerting the channel of the project's build condition. Jabber notifier: This notifier sends out a message to a specific Jabber account. MSN notifier: This notifier sends out a message to a specific MSN account. Wagon notifier: This notifier deploys the build results to a specific URL (for example, to the project's site) using Wagon. These notifiers can be configured to notify the project team on certain conditions of the build. They can be set up to Send on Success, Send on Failure, Send on Error, and/or Send on Warning as seen in the following screenshot: They are self-descriptive, but we will give each one a quick run through: Send on Success: Send out a notification if the build was successful. Send on Failure: "Code red! Code red! Code RED!!!". Need we say more? Okay, just to elaborate... a notification will be sent out when the build fails. By fail, we mean that a problem was encountered during build execution. This can be a compile failure, a test failure, or simply a dependency not being found. Send on Error: A notification will be sent out when there was an error outside of the build execution. A good example of this is when the source repository goes offline and Continuum cannot update its working copy. Send on Warning: A notification will be sent out if the build was not a successful build, a failed build, or an erroneous build. A notification is sent only when a project's state is modified. For example, if the previous state of all the projects in the group is SUCCESS and on the next build, all the projects in the entire group were built successfully again, no notification will be sent. However, if the second build of one of the projects failed (state is FAILURE), a notification will be sent specific to that project. The Send a mail to latest committers field for the Mail Notifier tells Continuum to send a notification mail to those developers who committed changes to the source repository as part of the last build. The Mail Recipient Address does not have any effect on the Send a mail to latest committers as the email addresses where the notification mail will be sent are retrieved from the list of project developers. Like build definitions, notifiers can also be configured at the project group level and/or at the project level. Notifiers are cumulative, which means notifiers configured at the group level always takes effect whether or not there are notifiers configured at the project level. This is similar to the co Now, let's create a group mail notifier for our centrepoint application. In our exercise, we will use our own valid email address for the mail notifier. We want to know everything that is happening to our project build so we tick all the checkboxes except for Send a mail to latest committers (we don't have usernames in our SVN repository so we don't have any use for this). Let's build our project by pressing the Build All Projects button on the Project Group Summary page. We should get a successful build and the build states of all the projects in the group should show the SUCCESS icon. However, no notification mail was sent. This is because there were not any changes in the build state nor were there any changes in SVN.
Read more
  • 0
  • 0
  • 2362
article-image-useful-maven-plugins-part-1
Packt
09 Nov 2009
13 min read
Save for later

Useful Maven Plugins: Part 1

Packt
09 Nov 2009
13 min read
Nobody can tell exactly how many Maven plugins exist today—since, like dependencies they can be retrieved from any specified remote repository, there are likely hundreds to choose from, and likely even more that have been custom written for use within the infrastructure of particular organizations. A common practice for frameworks and tools that require build integration is to publish a Maven plugin to accomplish the task—and it is becoming increasingly common to encounter this as a standard part of the getting started section of a project you might hope to use. However, there are also a number of plugins that would be considered general purpose and handle some extended build cases in a wider variety of projects. While this won't come close to covering all the plugins you are likely to encounter, with these common tools in your arsenal it will cover many of your Maven build needs, reducing the need for you to write your own plugins. The Remote Resources plugin Most projects will use the Resources plugin at some point, even if it isn't configured directly—it is standard in the default life cycle for any packaging that produces some type of artifact, bundling the resources found in src/main/resources. However, what if you wanted to share those resources among multiple projects? The best approach to doing that is to store the resources in the repository and retrieve them for use in multiple builds—and that is where the Remote Resources plugin comes in. First, we should note that this is not the only alternative for handling the scenario. The Dependency plugin's unpack goal is also quite capable of unpacking an artifact full of resources directly into the location that will be packaged. However, the Remote Resources plugin offers several advantages: Re-integration with the resources life cycle so that retrieved resources will automatically be processed in any goals in the process-resources phase. The ability to perform additional processing on the resources (including the optional use of Velocity templates to generate the resources) before inclusion. A specific bundle generation goal for creating the resource artifact in the first place. These advantages can make the plugin very effective at dealing with some common scenarios. For example the inspiration for the creation of the plugin, and one of its more common uses, is to place aggregated license files within the final artifact. There are other scenarios where the dependency:unpack goal remains more suitable. It is best to select the Remote Resources plugin when the files will be incorporated into the resources life cycle and the Dependency plugin when the files will be utilized independently. Let's look at how to create a license file for our Centrepoint application. We will do this in two steps—the creation of the resource bundle that provides the generic resources for any project by the same organization, and the processing of the module resources. Creating a Remote Resource bundle Remote Resource bundles are regular JAR files packaged with additional information generated by the remote resource plugin's bundle goal. Creating a module follows the same process as with other JAR files. In the example application, we will create the module outside of the Centrepoint multi-module hierarchy, so that it could (theoretically) be used by other projects from the same organization. This could be anywhere in source control, but we will assume it sits side-by-side with the effectivemaven-parent module in the workspace. $ mvn archetype:generate -DartifactId=license-resources -DgroupId=com.effectivemaven As this is not going to be a code project, the src/main/java and src/test directories can be removed from the generated content. We then continue to add the parent project to the POM, so the result looks like the following: <project xsi_schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd"> <modelVersion>4.0.0</modelVersion> <parent> <groupId>com.effectivemaven</groupId> <artifactId>effectivemaven-parent</artifactId> <version>1-SNAPSHOT</version> <relativePath>../effectivemaven-parent/pom.xml</relativePath> </parent> <artifactId>license-resources</artifactId> <version>1.0-SNAPSHOT</version> <name>License Resource Bundle</name> </project> We will add the Remote Resources plugin shortly, but first let's create the resources that will be bundled. These are added to the src/main/resources like regular resources. Consider the following Velocity template file, src/main/resources/LICENSE.vm: ## License Generator #macro(showUrl $url) #if($url) ($url) #end #end This software is distributed under the following license(s): #foreach ($l in $project.licenses) - $l.name #showUrl ($l.url) #end #if (!$projectsSortedByOrganization.isEmpty()) The software relies on a number of dependencies. The individual licenses are outlined below. #set ($keys = $projectsSortedByOrganization.keySet()) #foreach ($o in $keys) From: '$o.name' #showUrl($o.url) #set ($projects = $projectsSortedByOrganization.get($o)) #foreach ($p in $projects) - $p.name #showUrl ($p.url) $p.artifact #foreach ($l in $p.licenses) License: $l.name #showUrl ($l.url) #end #end #end #end For those not familiar with Velocity, the purpose of this is to first iterate through the project's licenses and list them, then secondly iterate through the project's dependencies (grouped by the organization they are from) and list their license. The $projectsSortedByOrganization variable is a special one added by the Remote Resources plugin to assist in this task. Before we can move on to use the bundle, we need to add the plugin to the bundle project like so: <build> <plugins> <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-remote-resources-plugin</artifactId> <version>1.0</version> <executions> <execution> <goals> <goal>bundle</goal> </goals> </execution> </executions> </plugin> </plugins> </build> This goal is required to generate a bundle manifest, the contents of which tell the plugin which resources to process when it is later called on to do so. With this all in place, we can now install the bundle into the local repository, ready for use: license-resources$ mvn install If you were to inspect the contents of the generated JAR file, you would see both the LICENSE.vm file in the root, and the bundle manifest in META-INF/maven/remoteresources.xml. You would also find that the Velocity template is unmodified—the contents will be executed when the bundle is later processed in the target project, which we will proceed to look at now. Processing Remote Resources in a project Using the resource bundle we have created is now quite straightforward. We start by adding the folllowing to the build section of modules/pom.xml file of Centrepoint: <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-remote-resources-plugin</artifactId> <version>1.0</version> <executions> <execution> <goals> <goal>process</goal> </goals> </execution> </executions> <configuration> <resourceBundles> <resourceBundle> com.effectivemaven:license-resources:1.0-SNAPSHOT </resourceBundle> </resourceBundles> </configuration> </plugin>   Here we have added a list of resource bundle artifacts to the configuration for the process goal, in the familiar shorthand artifact notation of groupId:artifactId:version. It has been added to the modules POM so that the license is included in the JAR files, but not included in the other non-code modules such as the documentation (which already generates a copy of the license from the reporting plugins). Normally, you should use a released version of the license bundle, not a snapshot as we have here (as we have not yet covered the release process!). Since the bundle is configured directly and not through a dependency, the Release plugin will not detect this unresolved snapshot later. Now, if we build a module such as store-api, we will see the license included in the root directory of the JAR file with the following content: This software is distributed under the following license(s): - The Apache Software License, Version 2.0 (http://www.apache.org/licenses/LICENSE-2.0.txt) The software relies on a number of dependencies. The individual licenses are outlined below. From: 'Apache Maven 2: Effective Implementations Book' (http://www.effectivemaven.com/) - Centrepoint Data Model com.effectivemaven.centrepoint:model:jar:1.0-SNAPSHOT License: The Apache Software License, Version 2.0 (http://www.apache.org/licenses/LICENSE-2.0.txt) From: 'Google' (http://www.google.com/) - Guice (http://code.google.com/p/google-guice/) com.google.code.guice:guice:pom:1.0 License: The Apache Software License, Version 2.0 (http://www.apache.org/licenses/LICENSE-2.0.txt) This is a good start, but we don't really need to include our own artifacts in the list, so we go back to the plugin declaration in modules/pom.xml and add another line of configuration: <configuration> <excludeGroupIds>${project.groupId}</excludeGroupIds> <resourceBundles> ... Regenerating the above artifact will alter the license to remove the dependencies from the project's group. A different case is the final distribution. As this is not part of the modules hierarchy, first we need to include the plugin definition identical to the one added previously. In the sample code for this article, you will notice that this has been taken a step further with the version and common configuration pushed into a pluginManagement section of the Centrepoint parent POM, and just the execution of the plugin goal remains in the modules and distribution POM files. We can now build the assembly as usual: distribution$ mvn clean install Upon inspecting the generated assemblies, you will not see the license file included yet. This is because the Assembly plugin does not pick up Maven resources by default, as it does not participate in the normal life cycle. To include the license file, we must alter the assembly descriptor distribution/src/main/assembly/bin.xml and add the following file set: <fileSet> <directory>target/maven-shared-archive-resources</directory> <outputDirectory>/</outputDirectory> </fileSet> The directory given is the standard location in which the Remote Resources plugin stores the resources it has processed, so if you decide to configure that differently in your own projects you would need to change this to the corresponding location. Upon building the assembly again we will see that the license has been generated, and that it includes the licenses of dependencies outside of the Centepoint application. As you can see, the distributed application depends on Jetty (also under the Apache License 2.0), which includes some portions of Glassfish (under the CDDL 1.0 License). While the above technique can be very helpful in constructing some useful information about your project and its dependencies, it cannot be guaranteed to produce complete licensing information for a project. The method relies on accurate information in the POMs of your dependencies, and this can sometimes be inaccurate (particularly when using public repositories such as the Maven Central Repository). If you are redistributing your files, always confirm that you have correctly recorded any necessary licensing information that must accompany them! The Remote Resources plugin is also capable of covering other scenarios that are particularly suited to license handling or more generally recording information about the project it is being processed for. These include: The supplementalDataModels configuration option that allows you to fill in incomplete or incorrect metadata for a project dependency before the resources are processed (to avoid particular problems as described above). The appendedResourcesDirectory, which allows you to store the above models in a separate file. The properties configuration, which allows the injection of other build properties into the Velocity templates. However, with this in mind, remember that the Remote Resources plugin is often just as suitable for any type of reusable resource, even if it is a static file. The Build Number plugin In Maven Mojos, the goals within a plugin are always designed to be simple tasks. Their aim is to do one thing, and do it well. A good example of this is the Build Number plugin. This simple plugin has one goal (create), with one purpose—to obtain a suitable build number and expose it to the build through properties or a file. While the plugin focuses on exposing the current Subversion revision, it is capable of generating an incremented build number (stored in a specified properties file), and a representation of the current system date and time. This feature can be very useful in identifying the exact heritage of a particular build. The build number generated by the plugin is different to that used by Maven to identify snapshots or artifact versions. While it is possible that you might mark your version using the information it generates, this plugin is typically used to record information about a particular build—whether it is a snapshot, or a release—within the artifact itself as a permanent record. Using the plugin is straightforward. By adding the goal to the project, the Subversion revision and a timestamp property will be exposed from the point that it is run onwards. <plugin> <groupId>org.codehaus.mojo</groupId> <artifactId>buildnumber-maven-plugin</artifactId> <version>1.0-beta-1</version> <executions> <execution> <phase>generate-resources</phase> <goals> <goal>create</goal> </goals> </execution> </executions> </plugin> In this example, we execute the plugin in the generate-resources phase so that the properties are available to any resource processing. Note that the values could be used with the Remote Resources plugin that we have just seen. There are two things to take into consideration with this configuration, however. Firstly, not all source builds will be Subversion checkouts, but the plugin does not verify that. To work around this potential problem, you can put the goal into a profile: <profile> <id>buildnumber</id> <activation> <file> <exists>.svn</exists> </file> </activation> <plugin> <groupId>org.codehaus.mojo</groupId> <artifactId>buildnumber-maven-plugin</artifactId> ... This particular activation check will cause the profile to be used within a Subversion checkout (that is, if .svn exists in the current directory), and to skip the plugin if not. In this case, the properties will not be set (or the file will not be created), so the code using these must take that into account. Secondly, how the values will be accessed needs to be given careful consideration. For example, it is unlikely that you want to create a convoluted build processing step to filter the value into a particular JSP file to appear in a web application. For the sake of keeping the build simple (and speedy), it is best to write the values into a single file that the application can then load from its classpath. This can be achieved by creating a filtered resource that contains references to the values. The advantage of this method is that it is automatic if you have already configured filtered resources, and automatically ends up in the classpath of the application code that can load the file as a resource.
Read more
  • 0
  • 0
  • 6649

article-image-including-charts-and-graphics-pentaho-reports-part-1
Packt
09 Nov 2009
8 min read
Save for later

Including Charts and Graphics in Pentaho Reports (Part 1)

Packt
09 Nov 2009
8 min read
Supported charts Pentaho Reporting relies on JFreeChart, an open source Java chart library, for charting visualization within reports. From within Report Designer, many chart types are supported. In the chart editor, two areas of properties appear when editing a chart. The first area of properties is related to chart rendering, and the second tabbed area of properties is related to the data that populates a chart. Following is the screenshot of the chart editor within Pentaho Report Designer: All chart types receive their data from three general types of datasets. The first type is known as a Category Dataset , where the dataset series and values are grouped by categories. A series is like a sub-group. If the exact category and series appear, the chart will sum the values into a single result. The following table is a simple example of a category dataset: Category Series Sale Price Store 1 Sales Cash $14 Store 1 Sales Credit $12 Store 2 Sales Cash $100 Store 2 Sales Credit $120 Pentaho Reporting builds a Category Dataset using the CategorySetDataCollector. Also available is the PivotCategorySetCollector, which pivots the category and series data. Collector classes implement Pentaho Reporting’s Function API. The second type of dataset is known as an XY Series Dataset, which is a two dimensional group of values that may be plotted in various forms. In this dataset, the series may be used to draw different lines, and so on. Here is a simple example of an XY series dataset : Series Cost of Goods (X) Sale Price (Y) Cash 10 14 Credit 11 12 Cash 92 100 Credit 105 120 Note that X is often referred to as the domain, and Y is referred to as the range. Pentaho Reporting builds an XY Series Dataset using the XYSeriesCollector. The XYZSeriesCollector also exists for three dimensional data. The third type of dataset is known as a Time Series Dataset , which is a two dimensional group of values that are plotted based on a time and date. The Time Series Dataset is more like an XY Series than a Category Dataset, as the time scale is displayed in a linear fashion with appropriate distances between the different time references. Time Series Sale Price May 05, 2009 11:05pm Cash $14 June 07, 2009 12:42pm Credit $12 June 14, 2009 4:20pm Cash $100 June 01, 2009 1:22pm Credit $120 Pentaho Reporting builds a Time Series Dataset using the TimeSeriesCollector. Common chart rendering properties Most charts share a common set of properties. The following properties are common across most charts. Any exceptions are mentioned as part of the specific chart type. Required Property Group Property name Description name The name of the chart object within the report. This is not displayed during rendering, but must be unique in the report. A default name is generated for each chart added to the report. data-source The dataset name for the chart, which is automatically populated with the name of the dataset in the Primary DataSource panel of the chart editor. no-data-message The message to display if no data is available to render the chart. Title Property Group Property name Description chart-title The title of the chart, which is rendered in the report. chart-title-field A field representing the chart title. title-font The chart title's font family, size, and style. Options Property Group Property name Description horizontal If set to True, the chart's X and Y axis are rotated horizontally. The default value is set to False. series-colors The color in which to render each series. The default for the first three series colors are red, blue, and green. General Property Group Property name Description 3-D If set to True, renders the chart in a 3D perspective. The default value is set to False. anti-alias If set to True, renders chart fonts as anti-aliased. The default value is set to True. bg-color Sets the background around the chart to the specified color. If not set, defaults to gray. bg-image Sets the background of the chart area to the specified image. If not set, the background of the chart area defaults to white. The chart area is the area within the axes of the chart. Supported image types include PNG, JPG, and GIF file formats. show-border If set to True, displays a border around the chart. The default value is set to True. border-color Sets the border to the specified color. If not set, defaults to black. plot-border If set to False, clears the default rendering value of the chart border. plot-bg-color Sets the plot background color to the specified color. If not set, defaults to white. plot-fg-alpha Sets the alpha value of the plot foreground colors relative to the plot background. The default value is set to 1.0. plot-bg-alpha Sets the alpha value of the plot background color relative to the chart background color. The default value is set to 1.0.   Legend Property Group Property name Description show-legend If set to True, displays the legend for the chart. The default value is set to False. location The location of the legend in relation to the chart, which may be set to top, bottom, left, or right. The default location is bottom. legend-border If set to True, renders a border around the legend. The default value is set to True. legend-font The type of Java font to render the legend labels in. legend-bg-color Sets the legend background color. If not set, defaults to white. legend-font-color Sets the legend font color. If not set, defaults to black. Advanced Property Group Property name Description dependencyLevel The dependency level field informs the reporting engine what order the chart should be executed in relation to other items in the report. This is useful if you are using special functions that may need to execute prior to generating the chart. The default value is set to 0. Negative values execute before 0, and positive values execute after 0.   Common category series rendering properties The following properties appear in charts that render category information: Options Property Group Property name Description stacked If set to True, the series values will appear layered on top of one another instead of being displayed relative to one another. stacked-percent If set to True, determines the percentages of each series, and renders the bar height based on those percentages. The property stacked must be set to True for this property to have an effect. General Property Group Property name Description gridlines If set to True, displays category grid lines. This value is set to True by default. X-Axis Property Group Property name Description label-rotation If set, adjusts the inline item label rotation value. The value should be specified in degrees. If not specified, labels are rendered horizontally. You must have show-labels set to true for this value to be relevant. date-format If the item value is a date, a Java date format string may be provided to format the date appropriately. Please see Java's SimpleDateFormat JavaDoc for formatting details. numeric-format If the item value is a decimal number, a Java decimal format string may be provided to format the number appropriately. Please see Java's DecimalFormat JavaDoc for formatting details. text-format The label format used for displaying category items within the chart. This property is required if you would like to display the category item values. The following parameters may be defined in the format string to access details of the item: {0}: To access the Series Name detail of an item {1}: To access the Category detail of an item {2}: To access the Item value details of an item To display just the item value, set the format string to "{2}". x-axis-title If set, displays a label describing the category axis. show-labels If set to true, displays x-axis labels in the chart. x-axis-label-width Sets the maximum category label width ratio, which determines the maximum length each category label should render in. This might be useful if you have really long category names. x-axis-label-rotation If set, adjusts the category item label rotation value. The value should be specified in degrees. If not specified, labels are rendered horizontally. x-font The font to render the category axis title and labels in. Y-Axis Property Group Property name Description y-axis-title If set, displays a label along the value axis of the chart. label-rotation If set, determines the upward angle position of the label, where the value passed into JFreeChart is the mathematical pie over the value. Unfortunately, this property is not very flexible and you may find it difficult to use. y-tick-interval The numeric interval value to separate range ticks in the chart. y-font The font to render the range axis title in. y-sticky-0 If the range includes zero in the axis, making it sticky will force truncation of the axis to zero if set to True. The default value of this property is True. y-incl-0 If set to True, the range axis will force zero to be included in the axis. y-min The minimum value to render in the range axis. y-max The maximum value to render in the range axis. y-tick-font The font to render the range tick value in. y-tick-fmt-str The DecimalFormat string to render the numeric range tick value. enable-log-axis If set to true, displays the y-axis as a logarithmic scale. log-format If set to true, will present the logarithmic scale in a human readable view.
Read more
  • 0
  • 0
  • 5208
Modal Close icon
Modal Close icon