blob: deaf7dce74b6c4a1f31ef92edb72b8042579004d [file] [log] [blame]
<?xml version="1.0"?>
<!--
Licensed to the Apache Software Foundation (ASF) under one
or more contributor license agreements. See the NOTICE file
distributed with this work for additional information
regarding copyright ownership. The ASF licenses this file
to you under the Apache License, Version 2.0 (the
"License"); you may not use this file except in compliance
with the License. You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing,
software distributed under the License is distributed on an
"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
KIND, either express or implied. See the License for the
specific language governing permissions and limitations
under the License.
-->
<document>
<properties>
<title>Migrating from 4.0 to 5.0</title>
</properties>
<body>
<section name="Introduction">
<p>
This document describes the basic steps needed to migrate an
application written for Turbine 4.0 to Turbine 5.0.
</p>
<p>
Migrating from Turbine 4.0 to Turbine 5.0 is mostly a task of
updating any references to commons-config and insuring that you
are using Parts for file upload rather than the old FileItem object.
</p>
</section>
<section name="Updating configuration">
<subsection name="Log4j changes">
<p>
TurbineResources.properties have changed making it less verbose
to point to the log4j config file.
Secondly, we have upgraded to Log4j2. We still keep the file as is (i.e. outside classpath), as this is what Turbine did all the way.
Old config line:
<source>
<![CDATA[
# -------------------------------------------------------------------
#
# L O G 4 J - L O G G I N G
#
# -------------------------------------------------------------------
log4j.file = WEB-INF/conf/log4j.properties
]]>
</source>
New config line:
<source>
<![CDATA[
# -------------------------------------------------------------------
#
# L O G 4 J 2 - L O G G I N G
#
# -------------------------------------------------------------------
log4j.file = log4j2.xml
]]>
</source>
</p>
</subsection>
<subsection name="Setting character encoding">
<p>
The default character encoding typically should be set
by your servlet container. For example, in Tomcat you can
update the server.xml in the following way to force URI connections
to be encoded in UTF-8.
</p>
<source>
<![CDATA[
<!-- Define a non-SSL HTTP/1.1 Connector on port 8080 -->
<Connector port="8080" maxHttpHeaderSize="8192"
maxThreads="150" minSpareThreads="25" maxSpareThreads="75"
enableLookups="false" redirectPort="8443" acceptCount="100"
connectionTimeout="20000" disableUploadTimeout="true"
URIEncoding="UTF-8"
/>
]]>
</source>
<p>
However, earlier versions of Apache Turbine allowed you to override this
value by setting the input.encoding property in your TurbineResources.properties file.
</p>
<p>
With Turbine 5.0, you can still accomplish this same behavior, but you need
to make sure that you have added the encoding valve to the turbine-classic-pipeline.xml
to enable it.
</p>
<p>
In TurbineResources.properties, you can set the following parameter:
</p>
<source>
<![CDATA[
input.encoding = UTF-8
]]>
</source>
<p>
But it will not be picked up unless you edit the pipeline valves
to read as follows. Note that ORDER does matter if you are not
seeing the behavior you expected.
</p>
<source>
<![CDATA[
<pipeline name="default">
<valves>
<!-- Use this valve to enable use of the input.encoding
parameter defined in your TurbineResources.properties file -->
<valve>org.apache.turbine.pipeline.DefaultSetEncodingValve</valve>
<valve>org.apache.turbine.pipeline.DetermineActionValve</valve>
<valve>org.apache.turbine.pipeline.DetermineTargetValve</valve>
<valve>org.apache.turbine.pipeline.DefaultSessionTimeoutValve</valve>
<valve>org.apache.turbine.pipeline.DefaultLoginValve</valve>
<valve>org.apache.turbine.pipeline.DefaultSessionValidationValve</valve>
<valve>org.apache.turbine.pipeline.DefaultACLCreationValve</valve>
<valve>org.apache.turbine.pipeline.ExecutePageValve</valve>
<valve>org.apache.turbine.pipeline.CleanUpValve</valve>
<valve>org.apache.turbine.pipeline.DetermineRedirectRequestedValve</valve>
</valves>
</pipeline>
]]>
</source>
</subsection>
</section>
<section name="Migrating to Functioal Interfaces and Rundata">
<p>Functional interfaces are now used instead of abstract classes. Rundata should be removed and instead of PipelineData used, but we keep it for now.
As a result <strong>AbstractValve was removed</strong> and with it the method <strong>getRunData(pipelineData)</strong> is gone.
You may retrieve the Rundata casted object now with <strong>pipelineData.getRunData()</strong>.
</p>
<source>
<![CDATA[
// old
// RunData rundata = getRunData(pipelineData)
RunData rundata = pipelineData.getRunData();
]]>
</source>
<p>
Assembler derived classes in package org.apache.turbine.modules (Action, LayoutScreen, Navigation, Page, ..)
are now declared as java functional interfaces instead of abstract classes.
Using them in child classes might be as easy as replacing <strong>extends</strong> with <strong>implements</strong> keyword in class declaration.
Remark: Method signature containing checked exceptions were not changed. To use Java 8 functional lamda functions you may
catch and rethrow them wrapped into a RuntimeException.
</p>
<source>
<![CDATA[
// old
// class MyAction extends Action
class MyAction implements Action
]]>
</source>
</section>
<section name="Migrating file upload to Parts">
<p>
In turbine-4.0.1 and prior, file uploads were processed through the
data.getParameters().getFileItem("file_field_name") method and returned
a FileItem object.
</p>
<p>
With Turbine-5.0, the framework is now using Java servlet 3.1.0.
As such, you will need to migrate this code using the
new Part object from the servlet spec. This actually saves you some
time since you don't have to convert the FileItem to a byte array and
then into an InputStream for processing.. you auto-magically get an
getInputStream() method on your javax.servlet.http.Part object to then
do as you please...
</p>
<source>
<![CDATA[
// all file items are now parts
Part fileItem = data.getParameters().getPart("file");
if (fileItem != null) {
InputStream is = fileItem.getInputStream();
BufferedReader bfReader = null;
try {
bfReader = new BufferedReader(new InputStreamReader(is));
String line = null;
while ((line = bfReader.readLine()) != null) {
// do something with the input here ...
}
} catch (IOException e) {
// handle exception here
e.printStackTrace();
} finally {
try {
if (is != null)
is.close();
} catch (Exception ex) {
// handle exception here
}
}
}
]]>
</source>
<p>
And if you really do need a byte array (for example to store the
contents as a binary object in the database), you can do this using the
following method calls.
</p>
<source>
<![CDATA[
InputStream is = fileItem.getInputStream();
byte[] byteArray = IOUtils.toByteArray(is);
]]>
</source>
</section>
<section name="Migrating to Quartz for Job Scheduling">
<p>
Quartz was introduced as a replacement for job scheduling starting
with Turbine 4.0. It provides Unix cron like abilities to manage
the automatic execution of tasks (or jobs) within your Turbine application.
</p>
<p>
An example of a job might be periodic data or log cleanup that you
want to automate within the application itself.
</p>
<subsection name="Creating a job">
<p>
Job creation has not really changed from the way jobs
were created in older versions of Turbine. You can refer
to the older documentation on how to create a job. For ease of
following the notes below however, we will add one simple job.
</p>
<p>
Create the job class in com.myapp.modules.scheduledjobs.MyTurbineJob.java
</p>
<source>
<![CDATA[
package com.myapp.modules.scheduledjobs;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.apache.turbine.modules.ScheduledJob;
import org.apache.turbine.services.schedule.JobEntry;
import org.apache.turbine.services.security.SecurityService;
public class MyTurbineJob extends ScheduledJob {
/** Logging */
private static Log log = LogFactory.getLog(MyTurbineJob.class);
// Track the number of times the job has been run
private static int taskcount = 0;
/**
* Constructor
*/
public MyTurbineJob() {
}
@Override
public void run(JobEntry job) throws Exception {
try {
log.info("Job run: " + taskcount);
// do some interesting work to be scheduled here...
} catch (Exception e) {
log.error("An error occurred running MyTurbineJob: " + e.toString());
}
taskcount++;
}
}
]]>
</source>
</subsection>
<subsection name="Quartz service configuration">
<p> The Quartz scheduler is implemented as a Fulcrum component and needs
to be defined in the roleConfiguration.xml and componentConfiguration.xml
files in the WEB-INF/conf directory.
</p>
<p> Modify the
<b>WEB-INF/conf/roleConfiguration.xml</b>
</p>
<source>
<![CDATA[
<role-list>
...
<!-- Service required for the QuartzSchedulerService -->
<role
name="org.apache.fulcrum.quartz.QuartzScheduler"
shorthand="quartz"
default-class="org.apache.fulcrum.quartz.impl.QuartzSchedulerImpl" />
</role-list>
]]>
</source>
<p> Modify the
<b>WEB-INF/conf/componentConfiguration.xml</b>
</p>
<source>
<![CDATA[
<componentConfig>
...
<quartz>
<configuration>
<properties>
<parameter name="org.quartz.scheduler.instanceName" value="TestScheduler"/>
<parameter name="org.quartz.scheduler.instanceId " value="AUTO"/>
<parameter name="org.quartz.scheduler.skipUpdateCheck" value="true"/>
<parameter name="org.quartz.threadPool.class" value="org.quartz.simpl.SimpleThreadPool"/>
<parameter name="org.quartz.threadPool.threadCount" value="3"/>
<parameter name="org.quartz.threadPool.threadPriority" value="5"/>
<parameter name="org.quartz.jobStore.misfireThreshold" value="60000"/>
<parameter name="org.quartz.jobStore.class" value="org.quartz.simpl.RAMJobStore"/>
<parameter name="org.quartz.plugin.jobInitializer.class" value="org.quartz.plugins.xml.XMLSchedulingDataProcessorPlugin"/>
<parameter name="org.quartz.plugin.jobInitializer.fileNames" value="../conf/quartz.xml"/>
<parameter name="org.quartz.plugin.jobInitializer.failOnFileNotFound" value="true"/>
<parameter name="org.quartz.plugin.jobInitializer.scanInterval" value="120"/>
<parameter name="org.quartz.plugin.jobInitializer.wrapInUserTransaction" value="false"/>
</properties>
</configuration>
</quartz>
</componentConfig>
]]>
</source>
<p>
Next, enable the Quartz scheduler service in the TurbineResources.properties file.
</p>
<source>
<![CDATA[
services.SchedulerService.classname=org.apache.turbine.services.schedule.QuartzSchedulerService
]]>
</source>
<p>
Finally, we need the configuration file for Quartz itself to run the job. You can do this by creating a quartz.xml file in your WEB-INF/conf directory.
</p>
<p>
If you want to change the name, check the component configuration above
which defines the org.quartz.plugin.jobInitializer.fileNames value.
</p>
<p>
This example quartz configuration is set to run our test job every 15 minutes.
</p>
<source>
<![CDATA[
<?xml version="1.0" encoding="utf-8"?>
<!-- Licensed to the Apache Software Foundation (ASF) under one or more contributor
license agreements. See the NOTICE file distributed with this work for additional
information regarding copyright ownership. The ASF licenses this file to
you under the Apache License, Version 2.0 (the "License"); you may not use
this file except in compliance with the License. You may obtain a copy of
the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required
by applicable law or agreed to in writing, software distributed under the
License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS
OF ANY KIND, either express or implied. See the License for the specific
language governing permissions and limitations under the License. -->
<job-scheduling-data
xmlns="http://www.quartz-scheduler.org/xml/JobSchedulingData"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.quartz-scheduler.org/xml/JobSchedulingData http://www.quartz-scheduler.org/xml/job_scheduling_data_2_0.xsd"
version="2.0">
<pre-processing-commands>
<delete-jobs-in-group>*</delete-jobs-in-group>
<!-- clear all jobs in scheduler -->
<delete-triggers-in-group>*</delete-triggers-in-group>
<!-- clear all triggers in scheduler -->
</pre-processing-commands>
<processing-directives>
<!-- if there are any jobs/trigger in scheduler of same name (as in this
file), overwrite them -->
<overwrite-existing-data>true</overwrite-existing-data>
<!-- if there are any jobs/trigger in scheduler of same name (as in this
file), and over-write is false, ignore them rather then generating an error -->
<ignore-duplicates>false</ignore-duplicates>
</processing-directives>
<schedule>
<job>
<name>MyTurbineJob</name>
<group>TURBINE</group>
<description>Perform some task at a specified time</description>
<job-class>org.apache.turbine.services.schedule.JobEntryQuartz</job-class>
</job>
<trigger>
<simple>
<name>MyTurbineJobTrigger</name>
<group>TURBINE</group>
<job-name>MyTurbineJob</job-name>
<job-group>TURBINE</job-group>
<start-time>2018-06-01T00:00:00</start-time>
<misfire-instruction>MISFIRE_INSTRUCTION_RESCHEDULE_NOW_WITH_EXISTING_REPEAT_COUNT</misfire-instruction>
<repeat-count>-1</repeat-count>
<!-- check every 15 minutes -->
<repeat-interval>900000</repeat-interval>
</simple>
</trigger>
</schedule>
</job-scheduling-data>
]]>
</source>
</subsection>
</section>
</body>
</document>