<init-param>
<param-name>name-of-parameter</param-name>
<param-value>value-of-parameter</param-value>
</init-param>
21 March 2016
This page contains answers to common questions related to the TVC components.
Yes. With the exception of some components like:
XBOM Manager
The XBL feature in XBOM Manager could be used but the installation of XBOM will fail due to missing datamodel entities such as type-Part and relationship-EBOM. This issue will be fixed in the future.
ECO/ECR Manager
MCAD Optimizer
Office Integration works, but will require some additional configuration as the CDM (Common Document Model) is not in place. |
Some additional configuration is required though.
You need to add some init-parameters to WEB.XML within the TVC Servlet definition. The init parameters are defined as below:
<init-param>
<param-name>name-of-parameter</param-name>
<param-value>value-of-parameter</param-value>
</init-param>
Following table shows the parameters you need, and some description:
Parameter Name | Description | Example |
---|---|---|
tvc.core.host |
The connection to Matrix (leave empty for RIP mode) |
rmi://hostname:1099 |
tvc.core.dateTimeFormat |
The date format used in Matrix (used when parsing date values from/to Matrix into Java date objects) |
yyyy-MM-dd HH:mm:ss |
tvc.core.inputDateTimeFormat |
The input date format |
yyyy-MM-dd HH:mm:ss |
ematrix.web.app |
Just set this value to TRUE as this is related to how TVC generates links |
TRUE |
ematrix.page.path |
Specify the "context" path to the application. Normally, this should be the value as obtained from "request.getContextPath()" but due to the way AEF/BPS is configured this parameter must be set here too. |
/application |
ematrix.login.page |
The login page that is used in the application. |
/login.jsp |
tvc.core.setRequestCharacterEncoding |
This setting will (if you use Tomcat) set the character encoding to the request object. + This is handy, as you otherwise would need either a filter (as used in ENVOIA), or modify the server.xml (Tomcat config file) to specify the requestURI encoding. |
true |
There is no automated process for this, it has to be done manually.
From the web-application, remove these files (or folders)
/tvc
/WEB-INF/tvc-*.tld
/WEB-INF/tvc
/WEB-INF/lib/tvc-*.jar
/WEB-INF/classes/tvc.license
Remove the schema entities
This is a bit trickier, but not impossible.
There is a program in the database that might help you. It’s not 100% reliable (and it is not supported really). The program is called TVCSchemaUninstaller |
Basically, what it does is:
Export the TVC Admin vault
Clear and Tidy this vault
Delete the vault
Delete roles
Delete TVC Shadow Agent User
Delete attributes
Delete policies
Delete types (start from the lowest type in the tree hierarchy)
Yes.
This can be done in a couple of different ways, see explanation below:
Add init parameters to the tvc-servlet within "web.xml" like the example below:
<init-param>
<param-name>tvc.core.user</param-name>
<param-value>User Agent</param-value>
</init-param>
<init-param>
<param-name>tvc.core.password</param-name>
<param-value>the-password</param-value>
</init-param>
This alternative is sometimes not OK as you store the password within the web.xml file in clear text.
Instead you can do:
Modify a program called "TVCInitSystemUser".
This program holds the user-name and password (in encrypted format) as properties, and is read when TVC is started (unless you have configured a user/password as described above).
From MQL; you can do:
pri program TVCInitSystemUser;
This gives something like:
program TVCInitSystemUser
mql
execute immediate
execute user
description
code '#@ version 1.0 name TVCInitSystemUser symname program_TVCInitSystemUser
tcl;
eval {
set sProgName [DEV:mql get env 0]
set sUserName [mql pri program $sProgName select property[DEV:USER_NAME].value dump |mql pri program _sProgName select property-(USER_NAME-).value dump -]
set sUserPass [mql pri program $sProgName select property[DEV:USER_PASS].value dump |mql pri program _sProgName select property-(USER_PASS-).value dump -]
return "$sUserName|$sUserPass"
}'
nothidden
property USER_NAME value TVC Shadow Agent
property USER_PASS value =uvZVYq2ODJU=
property application value TVC core
property installed date value 11-19-09
property installer value Technia AB
property original name value TVCInitSystemUser
property version value 1.0
created 11/19/2009 10:49:18 PM
modified 1/20/2010 2:03:23 PM
You could change the USER_NAME
property and USER_PASS
property.
The value for the latter is the encrypt password value.
tcl;
mql mod program TVCInitSystemUser property "USER_NAME" value "User Agent";
mql mod program TVCInitSystemUser property "USER_PASS" value [mql encrypt password "the-password"];
The role/group assignment to a person is cached in TVC in order to reduce the need for accessing the DB to get this information. Clearing the TVC Cache will of course clear this info, however, it will also clear everything that doesn’t need to be cleared.
A better alternative is to use the below methods:
com.technia.tvc.core.db.model.PersonInfo.clearCacheForPerson(String userName)
Or, use this method:
com.technia.tvc.core.db.model.PersonInfo.reloadCacheForPerson(String userName)
Alternatively, you can also do this for all persons by using one of the two alternatives below:
com.technia.tvc.core.db.model.PersonInfo.clearCache()
Or,
com.technia.tvc.core.db.model.PersonInfo.reloadCache()
The first call is fast, while the latter will take some time as it needs to reload the cache for all persons.
The "core" class in TVC that does the name lookup is called
com.technia.tvc.core.db.aef.naming.Naming
This method has a couple of methods for resolving symbolic names. All these are cached after first retrieval (until the TVC cache is cleared). The drawback with these methods (on the Naming class) is that those requires a "Transactional Context" to be allocated.
These classes serves different purposes.
SymbolicName is a wrapper class around the "Naming" and it represents a single symbolic-name / resolved-name value.
The SymbolicName caches the resolved value and sub-sequent retrieval of the value does not require transactional context to be available. Normally, the SymbolicName is used as "static final" fields in a class/interface:
public static final SymbolicName type_Part == new SymbolicName("type_Part");
One can provide a default value as the second argument, in order to use this value if the name-lookup fails for some reason.
To get the real name, simply do:
type_Part.get()
The ParsedStatement
is typically used to define select statements
containing symbolic names. For example:
private static final ParsedStatement documentClassification =
new ParsedStatement("$<from[relationship_DocumentRel].to.attribute[attribute_Classification]>");
To get the real select statement, one simply do:
documentClassification.get();
It works in a similar way as SymbolicName; e.g. the parsing is done once only.
The rule for both SymbolicName and ParsedStatement is that these should be used as static-final fields. If used as field in a method, it’s advantage is less as the parsing will be done more than one time for the same expression.
No. TVC is not dependent on any class within the AEF/BPS nor any central (except for some data model specific code, which exists in our XBOM Manager and ECO/ECR Manager components).
The only dependency TVC has, is against the ENOVIA-ADK classes.
Also, the TVC caches are better and more intelligent. The ENOVIA caches are loaded on server startup, which is the reason why the first login takes huge amount of time.
TVC caches are loaded on demand.
The TVC installation adds commands to a menu called “Admin Tools”, including the “Clear TVC Cache” command. But this depends on that menu (menu_AEFAdminToolsMenu) existing.
If, for some reason, that’s not there (due to access, or other reasons), it can be done manually by using this URL:
/tvc-action/clearCache
Basically, the columns are created by the so called TableFactory
.
You can specify your own factory via parameter:
tableFactory=com.acme.foo.MyTableFactory
Implement the table factory:
Extend TableFactory to implement loadTable. You can create a configurable table using a normal table definition as template and dynamically add columns to that definition.
private static class MyTableFactory extends TableFactory {
public Table loadTable(Object resource, Environment env) throws TVCException {
TableFactory baseFactory = TableFactory.getInstance();
Table base = baseFactory.loadTable(tableBase, env);
ConfigurableTable table = ConfigurableTable.createFromTemplate(base, env);
ColumnSettingsImpl cs == new ColumnSettingsImpl();
s.setNoWrap(false);
ConfigurableColumn col == table.addColumn(cs,env);
col.setExpression(…);
col.init();
return table;
}
}
In short:
Includes info about the selected rows from the structure browser in the URL specified in the command (request parameter: emxTableRowId)
Includes the object-id of the object used to launch the structure browser for. (request parameter: objectId)
Detailed:
When the user wants to perform an action (such as invoking a command from the context-menu or the tool-bar), the action typically applies to the selectedrows in the structure browser.
In order to instruct the Structure Browser to include these selected rows in the request when calling the action, one need to set the "Submit" setting on the command to TRUE. Note: In case you use XML commands/menus, the tag is called <SubmitForm>.
This implies that all the selected rows are submitted under the request parameter "emxTableRowId" (same as used in the centrals). The format of this parameter is:
emxTableRowId=<object-id>
In case when the row(s) only has information about the object
emxTableRowId=<relationship-id>|<object-id>
In case when the row(s) has information about both a connection and object
emxTableRowId=<relationship-id>|<object-id>|<parent-object-id>
This format was introduced in TVC 2009.1.0 in order to improve compatibility with AEF (or BPS) as they include the parent object-id also.
This format will be used in case when you submit from a strucuture page
in the ENOVIA structure browser, there is a fourth parameter that looks like |1,0. Currently, we don’t support this as the meaning of this parameter is not well defined. This however might lead to problems invoking an action that is used in the OOTB ENOVIA Structure Browser. |
If you set the "Submit OID" setting to true, the id of the object used to launch the structure browser with, will be included in the request. That id will be passed using the "objectId" request parameter.
The reason why we don’t submit this always, is that some commands work in the way to first check the "objectId" parameter and if not found check the "emxTableRowId" parameter. If we always would pass "objectId", those commands would start to behave wrongly.
Yes. This is achieved by implementing a so called "TableModel" or "TreeTableModel" (similar to what’s in the javax.swing world).
Flat List View = table-model Structure View = tree-table-model
Depending on the model used; one will need to use different actions (URLs):
/tvc-action/tableWithModel producer=com.acme.producer.MyTableModelProducer
And
/tvc-action/structureWithModel producer=com.acme.producer.MyTreeModelProducer
Where "producer" is a request parameter holding a class name.
The table-model producer interface is defined like:
package com.technia.tvc.structurebrowser.model;
public interface TableModelProducer {
TableModel produce(HttpServletRequest req) throws TVCException;
}
The tree-table-model producer interface is defined like:
package com.technia.tvc.structurebrowser.model;
public interface TreeTableModelProducer {
TreeTableModel produce(HttpServletRequest req) throws TVCException;
}
The TableModel and TreeTableModel interfaces are defined in the following packages:
com.technia.tvc.core.table.model
com.technia.tvc.core.structure.model
There are some abstract implementations of these interfaces, which makes it easier for you to implement your own code. |
Look for AbstractTableModel
, SimpleTableModel
and AbstractTreeTableModel
in the Java Docs available in TVC Developer
docs.
No, this is not configurable today. The target table-bean is a "clone" of the table-bean that you start the build-structure from.
We however do some changes to the cloned copy, such as changing the header/sub-header and modifying some properties on the "Page Config" instance.
If you want to customize this; then you have to do some (minor) coding:
Create an action class that is derived from the base class
com.technia.tvc.structurebrowser.buildstructure.actions.StartBuildStructureAction
Maybe call this com.acme.actions.CustomBuildStructureAction
Add a mapping to this action class within your own "Action mapping" file (never change the one in the component) Define this like the example below:
<action path="/customBuildStructure" type="com.acme.actions.CustomBuildStructureAction">
<forward name="frameset" path="/tvc/structurebrowser/buildstructure/tvcSearchFS.jsp" />
<forward name="autoLoad" path="/tvc/structurebrowser/buildstructure/tvcSearchFS.jsp?autoLoad=true"/>
</action>
Finally, overload the method getTargetTable(HttpServletRequest)
like below:
protected TableBean<?> getTargetTable(HttpServletRequest request) throws TVCException {
TableBean<?> t == super.getTargetTable(request);
/*
* Do whatever you need here...
*/
return t;
}
Since a pageconfig can have multiple views with multiple tables, it rely upon column settings instead of using an href parameter (as used by the ENOVIA tables); it would be difficult to define default sort column for all tables in all views using just request parameters. To use, add the Sort Direction setting to the column that you would like to sort on by default; if you have multiple columns that should be sorted on, also use the Sort Order setting to define the order of the sort.
Note that SB will remember which columns a user sorted on and auto apply these on the next visit, so the default sort settings will typically only apply for your first visit to a page, after that it will use the sort order from the personalization data.
Remembering column sorting and column visibility on user level can be globally switched on/off using the following init parameter to the tvc-servlet in the "web.xml":
<init-param>
<param-name>tvc.structurebrowser.rememberSelections</param-name>
<param-value>true</param-value>
</init-param>
The default value is true.
It’s also possible to control this on a pageconfig level by adding the "selectionTracking" parameter:
<PageConfiguration>
<Parameters>
<Parameter name="selectionTracking" value="false" />
</Parameters>
</PageConfiguration>
The global setting in combination with the setting per pageconfig decide whether user selections should be remembered or not.
Add the postprocess expandAll parameter to your pageconfig parameters or command URL
The reason below is that you are using a custom datahandler. The default datahandler usually takes care of the editable program invocation. My suggestion is that you add the editable logic directly to the custom datahandler or invoke the JPO from there.
<Column usesBusinessObject="false">
<DataHandlerClass>com.MyHandler</DataHandlerClass>
<EditableProgram>MyJPO</EditableProgram>
<EditableFunction>myMethod</EditableFunction>
</Column>
This feature is only available for right click context menus. For toolbar actions you need to handle this in the action (and notify user?).
Try using a selection behaviour to update values in bean. Then use bean values rather than request parameters in your actions.
TVC has a built in logic to prevent memory leakage problems from double clicks in the category tree. The problem occurs as structure browser table beans are loaded into session but never removed as the forward to the jsp registering the onunload event is never rendered because of the new request.
If you have any problems with the functionality you can turn it off using the init parameter
tvc.structurebrowser.preventDblClickFromCategoryTree=false
The renderer of the whole form is not possible to customize right now, but it might be something we will consider to allow in the future
To make the form forward to your own jsp, you can specify a custom processor for the form, and override the getForward() method, which is responsible for specifying where to forward after the form has been processed. Add the element <Processor>JAVACLASS</Processor>, pointing to a java class extending in this case the CreateObject class, since it’s a create form, and implementing the getForward method.
I’m creating “Create And Connect” Form but I have problems in it.
I was wondering if I’m able call TVC CreateAndConnect Form using Schema Command object? My problem is that it’s calling this form alright but for some reason it’s not connecting newly created object to object where call is made from.
_For some reason it seems that it’s not passing parent object id to CreateAndConnect Form. _
Maybe you’re just missing this:
<setting>
<name>Submit OID</name>
<value>true</value>
</setting>
TVC Forms have support for processors. A processor is what contains the main logic for whatever the result of the form should be. In the case of create forms, there is a processor used that simply creates objects and sets the attributes and basics for it.
If some special logic, such as a custom name generator, is needed when creating objects, it is possible to create your own form processor:
<Form>
<FormProcessor>com.acme.MyProcessor</FormProcessor>
...
</Form>
The specified class must extend from the class com.technia.tvc.structurebrowser.form.process.FormProcessor
.
Here is a simple example:
public class ExampleProcessor extends FormProcessor {
public ExampleProcessor(Form fc) { super(fc); }
@Override
public List<String> perform(Form form) throws Exception {
List<String> resultIds = new ArrayList<String>();
// Create an object
BusinessObject bo = BusinessObjectUtils.create(type,
name, revision, basics.getVAULT(), basics.getPOLICY(),
TxManager.getCurrentUser(), objectAttributeList);
BusinessObjectUtils.setDescription(bo, basics.getDESCRIPTION());
// Add the object id to the results list
resultIds.add(bo.getObjectId());
return resultIds;
}
}
The name is legacy, and originates from TVC Report Generator 3.0 (2003).
The conversion properties is a multiline attribute on the report-definition object.
The original idea was that this attribute will hold properties/settings that controls the data-conversion, but the scope of this attribute has grown and is used for many purposes. Example:
Defining the name of the generated report:
Defining the file prefix of the generated report (file.prefix=ECO_Report)
Defining the file suffix of the generated report (file.suffix=pdf)
Defining the content-type of the generated report(file.contentType=application/pdf)
Whether or not to use FOP 0.20.5 or the latest FOP version (useLatestFOP=true|false) The default value for this can be specified globally for the report generator.
Whether or not to generate the report "on demand" (file.on.demand=true|false)
Whether or not to extract the data, which the report is based upon, as a super user (createAsSuperUser=true|false)
Override the converter, e.g. specifying a custom Java class (converterClass=com.acme.MyConverter)
Must implement the interface com.technia.tvc.reportgenerator.Converter
Transaction type during dataextraction. In some rare cases, one will do some update to the database and that requires an update transaction to be started (transactionType=read|update|none)
Defining post processors (postProcessors=pdf-stamp,zip)
The post processors are invoked after the report has been created, but before any output handler is processed
Defining output handlers (outputHandlers=mail,checkin,custom)
The outout handler typically delivers the generated report somewhere
Specifying the name of the queue, in which the report will be produced (queue.name)
The report generator has a mechanism for distributing the creation of a report into one/several different processes in order to reduce load on the application servers. Specifying a pre-processor (*Note: this is not documented yet) that handles pre-processing when creating the report such as defining custom page to display (preProcessorClass=com.acme.MyPreProcessor)
The pre processor must implement the interface com.technia.tvc.reportgenerator.preprocess.PreProcessor
Yes.
The purpose with data handlers is to create "Cell" and populate these with value(s) (a Cell can have multiple CellValue’s associated). The report generator produces output according to what the cell’s on a row contains, and this typically looks like:
<cell>
<value oid="..." rid="..." type="...">...</value>
<value oid="..." rid="..." type="...">...</value>
</cell>
Note that a value in a cell can be associated with a different object, which in turn results in that the oid/rid/type attribute is added.
The value itself is escaped by default, as the value might contain characters that causes the XML data to be invalid. If you want to populate a cell with vaules that represents XML data, you can do this by applying the setting:
Preserve Output = TRUE
The value that is being added, is retrieved from the method "getDisplayValue" on the CellValue instance.
In many cases, it is very useful to implement a datahandler that produces XML data, for reference see Report Generator Data Handler Example on how to implement such a datahandler in the report generator.
As usual, you have a number of alternatives to choose from:
An alternative to creating HTML output, is to create XML reports that follows the SpreadsheetML format.
For more information regarding SpreadsheetML, see this URL: SpreadsheetML @ MSDN
In order to be able to produce SpreadsheetML, one need to make sure that the following properties are set on the report definition:
Output Format = XML
Displayed Output Format = Excel
Stylesheet = <name of stylesheet that produces StylesheetML output>
In addition, you probably have to play around with the so called "conversion properties"
file.contentType=application/vnd.ms-excel (in order to force Excel to open the file)
Implementing a custom report, that produced binary output with help from for example POI: POI @ Apache
You need to implement a Java class that is found from this link: <Custom Report Example>
Finally, you need to specify the name of the class within the report definition (of type TVC Custom Report)
The original idea with the TVC Report Generator was to generate XML over a set of objects by using a table (or system table) and convert this XML into some other format using a so called converter. The most famous examples are:
Convert the raw XML to XSL-FO and use FOP to produce for example PDF from the XSL-FO
Convert the raw XML to HTML directly (either creating a HTML report, or a HTML page that Excel can interpret)
The good thing with XML/XSLT is that you can modularize the report and reuse pieces like tables, datahandlers, inquiries etc and only spend time on developing the stylesheets.
If you want to create reports without the influence of XML/XSLT, then you need to do slightly more coding than you otherwise would have needed. In some cases, the time of doing that is shorter than developing stylesheets but sometimes not.
Anyhow, what you are looking for is achieved by implementing a so called Custom Report, which uses a custom Java class for producing the report. Please look at the FAQ Custom Report Example for details.
Again, this approach requires you to implement data-extraction etc. Of course, you can utilize the TVC API to load inquiries, performing expansions and evaluate a table over a flat-list or structure.
An alternative, which somehow requires you to parse XML, is to use a custom converter.
converterClass=com.acme.MyConverter
This class should implement this interface:
com.technia.tvc.reportgenerator.Converter
It has one method that needs to be implemented, and some skeleton code is shown below:
public void convert(ConverterCtx ctx) throws IOException {
InputStream in == ctx.getInput();
OutputStream out == ctx.getOutput();
Report report == ctx.getReport();
// Read XML data from the input stream
// Do whatever you need with this data,
// and write the generated report to the outputstream
// You can from the Report instance get settings/properties // defined
in there.
}
Depending on what output you create, you need to set this on the report as well:
file.contentType=application/vnd.ms-excel file.prefix=report
file.suffix=.xls
What you can do here, if you need more advanced logic for creating the groups, is to create a column with a datahandler in the table used for the report.
Include the grouping/formatting logic in this column so that the result is the grouping you need. Then use that column to group, and just hide it from the rest of the report (in the stylesheet, just skip that column).
You can use the Custom report. Depending on what output you want, you need to specify what converter to be used.
This example assumes that you will produce XSl-FO with your stylesheet and use a compatible output format (PDF for example):
import com.technia.tvc.core.TVCException;
import com.technia.tvc.core.html.io.XMLWriter;
import com.technia.tvc.reportgenerator.Converter;
import com.technia.tvc.reportgenerator.DataExtractor;
import com.technia.tvc.reportgenerator.DataExtractorCtx;
import com.technia.tvc.reportgenerator.convert.FOConverter;
import com.technia.tvc.reportgenerator.impl.CustomReport;
import java.io.IOException;
public class ExampleWithFOConversion extends CustomReport implements DataExtractor {
/**
* Don't forget this field if you want to run this report distributed
*/
private static final long serialVersionUID == 1L;
@Override
public DataExtractor getDataExtractor() {
return this;
}
public void extract(DataExtractorCtx ctx) throws TVCException, IOException {
XMLWriter writer == ctx.getXMLWriter();
/*
* Construct your XML data here through the XMLWriter
*/
writer.startElement("my-root")
.startElement("sub")
.addText("text")
.endElement()
.startElement("next-sub")
.addAttribute("foo", "bar")
.addText("hello world")
.endElement()
.finish();
}
@Override public Converter getConverter() {
return FOConverter.getInstance();
}
}
Again, use the Custom Report as base. IText is part of the TVC-Core, so you don’t need any additional JARs for this.
See this example:
import com.technia.tvc.core.TVCException;
import com.technia.tvc.core.util.pdf.PDFPageSize;
import com.technia.tvc.reportgenerator.DataExtractor;
import com.technia.tvc.reportgenerator.DataExtractorCtx;
import com.technia.tvc.reportgenerator.impl.CustomReport;
import com.technia.tvc.lowagie.text.Document;
import com.technia.tvc.lowagie.text.DocumentException;
import com.technia.tvc.lowagie.text.Paragraph;
import com.technia.tvc.lowagie.text.pdf.PdfWriter;
import java.io.IOException;
public class ExampleWithIText extends CustomReport implements DataExtractor {
/**
* Don't forget this field if you want to run this report distributed
*/
private static final long serialVersionUID == 1L;
public void extract(DataExtractorCtx ctx) throws TVCException, IOException {
try {
extractImpl(ctx);
} catch (DocumentException de) {
throw new TVCException("unable to create pdf document", de);
}
}
}
protected void extractImpl(DataExtractorCtx ctx) throws DocumentException, IOException {
Document iTextDocument == new Document(PDFPageSize.LETTER.size());
/* PdfWriter writer == */PdfWriter.getInstance(iTextDocument, ctx.getOutputStream());
iTextDocument.open();
iTextDocument.newPage();
iTextDocument.add(new Paragraph("Hello World"));
iTextDocument.close();
}
@Override
public DataExtractor getDataExtractor() {
return this;
}
}
You need to point out the webapp directory using the environment
variable TVC_WEBAPP_DIR
set TVC_WEBAPP_DIR=<webapp directory goes here>
MQL<1>set context user creator
MQL<2>execute program TVCReportGenerator -method mxMain -oid=<object id goes here> -report=tvc:report:..../MyConf.xml
The most common error with the report generator is malformed XSLT/XML definitions. If you don’t have a good XML editor, these can sometimes be very frustrating and time consuming.
While a good editor is recommended there are always exceptions (even Eclipse sometimes chokes if you have a really bad mistake) where you just need a quick way of finding what is causing the error. The one trick that seems to be the quickest is to rename the XSLT to XML and open up in Microsoft Internet Explorer: IE will tell you exactly what is wrong and on what line.
Another great option is to simply copy your XML or XSLT into the W3C schools validator located at http://www.w3schools.com/xml/xml_valid http://www.w3schools.com/xml/xml_validator.asp[ator.asp]
Who is Gregor and why is he causing so many issues with my stylesheet? This is actually a limitation of the Sun XSLTC as it cannot handle a stylesheet over 64kb - yes, this could be a frustrating limit but then again your stylesheets should never get this large. To fix this issue, you will need to modularize your stylesheet and break it up into smaller files and/or templates instead of just one bit stylesheet
In most cases, errors that cause a corrupt 15 byte file can be found simply by turning TVC Debug On which then shows a more verbose message that typically shows you the stylesheet error that is causing the problem. However, on Websphere/AIX this may not always be true so you should also take a look at the system error log file.
One such error found recently is that if the xsl:sort is invalid (e.g. uses data-type="string" instead of data-type="text") then it causes the stylesheet to fail with no warning but the websphere error log file will show the message of "The string 'string' was used where a QName that has a prefix is required."
Skeleton class for custom reports.
public class Example extends CustomReport implements DataExtractor, Converter {
private static final long serialVersionUID = 1L;
public DataExtractor getDataExtractor() {
return this;
}
public void extract(DataExtractorCtx ctx) throws TVCException, IOException {
/*
* Get a reference to the object, which the report is being created for
*/
ObjectRef ref = ctx.getReport().getSourceObject();
...
/*
* If you generate binary data; obtain the output stream here
*/
OutputStream out = ctx.getOutputStream();
...
}
public Converter getConverter() {
/*
* If the data uses a converter, return it here.
* If not; simply return null.
*/
return this;
}
public void convert(ConverterCtx ctx) throws IOException {
// If you decide to implement the Converter interface, you need to do something here...
}
}
This datahandler already exists in TVC Report Generator; so you don’t have to compile this on your own. The class is here for reference only. |
The name of the class is com.technia.tvc.reportgenerator.datahandler.AttributesHandler
import com.technia.tvc.core.db.AttributeUtils;
import com.technia.tvc.core.db.DateUtils;
import com.technia.tvc.core.db.aef.AEFUtils;
import com.technia.tvc.core.db.model.AttributeConstants;
import com.technia.tvc.core.db.model.AttributeInfo;
import com.technia.tvc.core.db.model.RelationshipInfo;
import com.technia.tvc.core.db.model.TypeInfo;
import com.technia.tvc.core.db.select.Statement;
import com.technia.tvc.core.db.table.evaluator.DataHandler;
import com.technia.tvc.core.db.table.evaluator.EvaluatedData;
import com.technia.tvc.core.db.table.evaluator.EvaluationInput;
import com.technia.tvc.core.db.table.evaluator.SelectedData;
import com.technia.tvc.core.gui.table.Cell;
import com.technia.tvc.core.gui.table.Column;
import com.technia.tvc.core.gui.table.impl.StringCell;
import com.technia.tvc.core.html.io.XMLWriter;
import com.technia.tvc.core.html.io.XMLWriterFactory;
import com.technia.tvc.core.util.StringUtils;
import java.io.StringWriter;
import java.util.Date;
import java.util.Iterator;
import java.util.List;
import java.util.Locale;
/**
* This handler can be used to retreive all attributes/values for a relationship
* or object, and output the data in the cell as XML.
* <p>
* The generated XML has the following format:
*
*
* <attributes>
* <attribute name="..." type="..." localizedName="...">...</Attribute>
* <attribute name="..." type="..." localizedName="...">...</Attribute>
* ...
* </attributes>
*/
public class AttributesHandler implements DataHandler {
public void prepareEvaluation(Column col, EvaluationInput input) {
if (col.usesBusinessObject()) {
input.addSelectBus(Statement.TYPE);
input.addSelectBus(Statement.ATTRIBUTE_VALUE);
} else {
input.addSelectRel(Statement.TYPE);
input.addSelectRel(Statement.ATTRIBUTE_VALUE);
}
}
public Cell createCell(Column col, EvaluatedData data) {
return new StringCell(col);
}
public void populateCell(Cell cell, EvaluatedData data) {
boolean bus = cell.getColumn().usesBusinessObject();
SelectedData sd = bus ? data.getObjectData() : data.getRelationshipData();
List<String> attributes = null;
String typeName = sd.getSelectValue(Statement.TYPE);
if (!StringUtils.isOnlyWhitespaceOrEmpty(typeName)) {
try {
if (bus) {
attributes = TypeInfo.getInstance(typeName).getAttributeNames();
} else {
attributes = RelationshipInfo.getInstance(typeName).getAttributeNames();
}
} catch (Exception shouldNeverHappenNormally) {
}
}
if (attributes != null) {
StringWriter sw = new StringWriter();
XMLWriter xml = XMLWriterFactory.newWriter(sw);
xml.startElement("attributes");
String name, value, localizedName;
int type = AttributeConstants.TYPE_UNKNOWN;
Locale locale = cell.getColumn().getLocale();
for (Iterator<String> itr = attributes.iterator(); itr.hasNext();) {
name = itr.next();
value = sd.getSelectValue("attribute[" + name + "].value", true);
if (value != null) {
xml.startElement("attribute");
try {
AttributeInfo ai = AttributeInfo.getInstance(name);
type = ai.getType();
if (type == AttributeConstants.TYPE_DATETIME) {
if (!StringUtils.isOnlyWhitespaceOrEmpty(value)) {
Date d = DateUtils.parse(value);
xml.addAttribute("isoDateTime", DateUtils.formatISO8601DateTime(d));
xml.addAttribute("isoDate", DateUtils.formatISO8601Date(d));
value = DateUtils.formatForDisplay(d, locale);
}
}
} catch (Throwable t) {
}
xml.addAttribute("type", AttributeUtils.toString(type));
xml.addAttribute("name", name);
localizedName = AEFUtils.getLocalizedAttributeName(name, cell.getColumn()
.getLocale());
if (StringUtils.isOnlyWhitespaceOrEmpty(localizedName)) {
localizedName = name;
}
xml.addAttribute("localizedName", localizedName);
xml.addText(value);
xml.endElement();
}
}
xml.finish();
cell.addValue(sw.toString());
}
}
}
Implement a custom handler
public class Example extends Handler {
@Override
public TableBean<? extends TableData> create(HandlerCtx ctx) throws TVCException {
TableBean<?> t = DefaultHandler.getInstance().create(ctx);
for (TableData d : IteratorUtils.toList(t.getTableData())) {
for (Cell c : IteratorUtils.toList(d.getRow().getCells())) {
c.setEditable(true);
}
}
return t;
}
}