Error when trying to create a model in PowerDesigner through Java - java

The actual code
package OOM.src.createOOM;
import org.eclipse.swt.internal.ole.win32.COM;
import com.sybase.stf.powerdesigner.PdCommon.*;
import com.sybase.stf.powerdesigner.PdOOM.*;
/**
* #author Xiao Wang
*
* This Java sample program shows how to use PowerDesigner Java proxies and
* OLE automation to create a PowerDesigner OOM model and display the contains of the OOM model.
*/
public class CreateOOM {
/** PowerDesigner application object */
private Application pdApp;
private int nbClasses;
/**
* Program entry point
*/
public static void main(String[] args) {
System.out.println("shhs");
// Create an instance of this class
CreateOOM createOOM1 = new CreateOOM();
// Create an OOM and display the result
createOOM1.CreateAndDisplayOOM();
}
/**
* Create an OOM and display the result
*/
public void CreateAndDisplayOOM() {
int hr = COM.OleInitialize(0);
try {
// Get PowerDesigner application instance and start PowerDesigner if necessary
pdApp = Application.getInstance();
if (pdApp != null)
{
// Create an OOM
Model newModel;
newModel = createOOM();
// Show the information of the current OOM
showOOMInfo();
// Release PowerDesigner application.
// This may close PowerDesigner if PowerDesigner was started by this program.
// pdApp.Release();
}
}
catch (Exception e) {
System.out.println("Cannot create PowerDesigner application object. Please verify that PowerDesigner is installed.");
}
finally {
if (hr == COM.S_OK)
COM.OleUninitialize();
}
}
/**
* Create an OOM for Java
*/
public Model createOOM() {
try {
// Create an OOM model, use Java as the language, create a default class diagram
// Convert the return object to PdOOM.Model proxy object
Model newModel = new Model(pdApp.CreateModel(PdOOM_Classes.cls_Model, "|Language=Java|Diagram=ClassDiagram"));
// set name and code
newModel.SetName("Customer Management");
newModel.SetCode("CustomerManagement");
//System.out.println("reach");
// Create a customer class.
// Use the fully qualified name here to avoid conflict with Java
com.sybase.stf.powerdesigner.PdOOM.Class newClass1 =
new com.sybase.stf.powerdesigner.PdOOM.Class(newModel.GetClasses().CreateNew());
newClass1.SetName("Customer");
newClass1.SetCode("Customer");
newClass1.SetComment("Customer class");
Attribute newAttribute;
// Create an id attribute
newAttribute = new Attribute(newClass1.GetAttributes().CreateNew());
newAttribute.SetName("Id");
newAttribute.SetCode("id");
// id is the primary identifier (primary key)
newAttribute.SetPrimaryIdentifier(true);
// set the Java data type
newAttribute.SetDataType("int");
// Create a name attribute
newAttribute = new Attribute(newClass1.GetAttributes().CreateNew());
newAttribute.SetName("Name");
newAttribute.SetCode("name");
// set the Java data type
newAttribute.SetDataType("java.lang.String");
// Create a phone attribute
newAttribute = new Attribute(newClass1.GetAttributes().CreateNew());
newAttribute.SetName("Phone");
newAttribute.SetCode("phone");
// set the Java data type
newAttribute.SetDataType("java.lang.String");
// Create an email attribute
newAttribute = new Attribute(newClass1.GetAttributes().CreateNew());
newAttribute.SetName("Email");
newAttribute.SetCode("email");
// set the Java data type
newAttribute.SetDataType("java.lang.String");
// Create an SalesOrder class.
// Use the fully qualified name here to avoid conflict with Java
com.sybase.stf.powerdesigner.PdOOM.Class newClass2 =
new com.sybase.stf.powerdesigner.PdOOM.Class(newModel.GetClasses().CreateNew());
newClass2.SetName("SalesOrder");
newClass2.SetCode("SalesOrder");
newClass2.SetComment("Sales order class");
// Create an orderId attribute
newAttribute = new Attribute(newClass2.GetAttributes().CreateNew());
newAttribute.SetName("Order id");
newAttribute.SetCode("orderId");
// id is the primary identifier (primary key)
newAttribute.SetPrimaryIdentifier(true);
// set the Java data type
newAttribute.SetDataType("int");
// Create an orderDate attribute
newAttribute = new Attribute(newClass2.GetAttributes().CreateNew());
newAttribute.SetName("Order date");
newAttribute.SetCode("orderDate");
// set the Java data type
newAttribute.SetDataType("java.util.Date");
// Create an association
Association association1 = new Association(newModel.GetAssociations().CreateNew());
// Set linked classes
association1.SetObject1(newClass1);
association1.SetObject2(newClass2);
// Set role A name and multiplicity
association1.SetRoleAName("customer");
association1.SetRoleAMultiplicity("1");
// Set role B name and multiplicity
association1.SetRoleBName("orders");
association1.SetRoleBMultiplicity("0..*");
// Get the default class diagram
ClassDiagram newDiagram = new ClassDiagram(newModel.GetDefaultDiagram());
// show the symbol in the default diagram if it is a class diagram
newDiagram.AttachObject(newClass1);
newDiagram.AttachObject(newClass2);
newDiagram.AttachLinkObject(association1);
return newModel;
}
catch (Exception e) {
e.printStackTrace();
return null;
}
}
/**
* Display information about the currnt OOM
*/
public void showOOMInfo() {
try {
// Get the current OOM model
if (!pdApp.GetActiveModel().isNull())
{
if (pdApp.GetActiveModel().IsKindOf(PdOOM_Classes.cls_Model))
{
// Convert the active model to an OOM model proxy object
Model aModel = new Model(pdApp.GetActiveModel());
// Initialize the number of classes
nbClasses = 0;
// Display a message in PowerDesigner outut window
pdApp.Output("Display the list of classes in the system output window.");
// Show classes and packages defined under the model
// Convert model proxy object to package proxy object
showPackageInfo(new com.sybase.stf.powerdesigner.PdOOM.Package(aModel));
System.out.println("There are " + nbClasses + " class(es) in this model.");
}
else
{
System.out.println("The current model is not an OOM model.");
}
}
else
{
System.out.println("There is no active model opened in PowerDesigner.");
}
}
catch (Exception e) {
e.printStackTrace();
}
}
/**
* Display information about an OOM package
*/
public void showPackageInfo(com.sybase.stf.powerdesigner.PdOOM.Package aPackage) {
if (!aPackage.isNull() && !aPackage.IsShortcut())
{
// Display the number of classes and packages in the system output window
System.out.println("The " + aPackage.GetObjectType() + " '" + aPackage.GetCode() + "' contains " + aPackage.GetClasses().GetCount() + " class(es), " + aPackage.GetPackages().GetCount() + " package(s).");
// Use the fully qualified name here to avoid conflict with Java
com.sybase.stf.powerdesigner.PdOOM.Class aClass;
for (int n = 0; n < aPackage.GetClasses().GetCount(); n++)
{
nbClasses++;
if (nbClasses < 100)
{
// display class info
aClass = new com.sybase.stf.powerdesigner.PdOOM.Class(aPackage.GetClasses().Item(n));
showClassInfo(aClass);
}
else
{
if (nbClasses == 100)
System.out.println("...");
break;
}
}
// display classes of subpackages
com.sybase.stf.powerdesigner.PdOOM.Package subPackage;
for (int nPackage = 0; nPackage < aPackage.GetPackages().GetCount(); nPackage++)
{
subPackage = new com.sybase.stf.powerdesigner.PdOOM.Package(aPackage.GetPackages().Item(nPackage));
showPackageInfo(subPackage);
}
}
}
/**
* Display information about a class
*/
public void showClassInfo(com.sybase.stf.powerdesigner.PdOOM.Class aClass) {
try {
if (!aClass.isNull() && !aClass.IsShortcut())
{
System.out.println("Class No." + nbClasses + ": " + aClass.GetCode() + ", " + aClass.GetAttributes().GetCount() + " attribute(s), " + aClass.GetOperations().GetCount() + " operation(s)");
}
}
catch (Exception e) {
e.printStackTrace();
}
}
}
The issue im getting is--
org.eclipse.swt.SWTException: Action can not be performed. result = -2137456383 (com.sybase.stf.powerdesigner.com.COMException: COM Failure [HRESULT: 0x80990101])
at org.eclipse.swt.ole.win32.OLE.error(OLE.java:345)
at com.sybase.stf.powerdesigner.com.COMException.raiseOnFail(COMException.java:83)
at com.sybase.stf.powerdesigner.com.IDispatchEx.raisingInvoke(IDispatchEx.java:231)
at com.sybase.stf.powerdesigner.PdCommon.IApplication.CreateModel(IApplication.java:84)
at OOM.src.createOOM.CreateOOM.createOOM(CreateOOM.java:68)
at OOM.src.createOOM.CreateOOM.CreateAndDisplayOOM(CreateOOM.java:42)
at OOM.src.createOOM.CreateOOM.main(CreateOOM.java:28)
Caused by: com.sybase.stf.powerdesigner.com.COMException: COM Failure [HRESULT: 0x80990101]
at com.sybase.stf.powerdesigner.com.COMException.raiseOnFail(COMException.java:88)
... 5 more
There is no active model opened in PowerDesigner.
how can I resolve this?

Related

Generic Methods for Rendering TableColumns in JavaFX

So my application uses a number of TableViews within different FXMLViewControllers to present a number of different JPA Entities. The example below is for JobSupplierParts.
/**
* renderDoubleColumn takes a TableColumn setting its value and type before setting up edit event handling.
* #param column the tableColumn to be set up.
* #param field the name of the field to be mapped to.
* #param methodName the set method name of the field.
*/
protected void renderDoubleColumn(TableColumn<JobSupplierPart, Double> column, String field, String methodName) {
String className = "BiasDB.JobSupplierPart";
column.setCellValueFactory(new PropertyValueFactory<>(field));
column.setCellFactory(TextFieldTableCell.<JobSupplierPart, Double>forTableColumn(new DoubleStringConverter()));
column.setOnEditCommit(
new EventHandler<TableColumn.CellEditEvent<JobSupplierPart, Double>>() {
#Override
public void handle(TableColumn.CellEditEvent<JobSupplierPart, Double> t) {
JobSupplierPart supplierPart = t.getTableView().getItems().get(t.getTablePosition().getRow());
try {
Class<?> c = Class.forName(className);
Method method = c.getDeclaredMethod(methodName, Double.class);
method.invoke(supplierPart, t.getNewValue());
supplierPart.setTotal(updateItem(supplierPart));
} catch (ClassNotFoundException|NoSuchMethodException|IllegalAccessException|InvocationTargetException ex) {
logger.error("renderDoubleColumn",ex);
} //End try to get method from String.
try {
jobSupplierPartController.edit(supplierPart);
} catch (Exception ex) {
logger.error("renderDoubleColumn",ex);
}
t.getTableView().refresh();
}
} //END Event Handler
); //END SetOnEditCommit.
}
//END renderDoubleColumn
I can call this with:
renderDoubleColumn(discountColumn, "discount", "setDiscount");
BUT - I have to create new methods for each JPA Entity. Is it possible to replace the references to JobSupplierPart such that it becomes a generic method much like I have achieved with the methods? I have tried and alternatives such as T or K but they all returned errrors. The controller can just be passed as a parameter. Or is this a really bad practice/poor performance thing to do?
So I don't know if the Java aficionados will agree with this solution but in response to an answer posted and then deleted shortly after I was able to make the code cleaner. I also moved the set/edit section into a method so now I have:
/**
* renderBigDecimalColumn takes a TableColumn setting its value and type before setting up edit event handling.
* #param column the tableColumn to be set up.
* #param field the name of the field to be mapped to.
*/
private void renderBigDecimalColumn(TableColumn<AccountAsset, BigDecimal> column, String field) {
//Set an observable value for the column
column.setCellValueFactory(new PropertyValueFactory<>(field));
//Set how we want the cell to be rendered
// This line varies for the different cell types e.g. Strings, Bools etc.
column.setCellFactory(TextFieldTableCell.<AccountAsset, BigDecimal>forTableColumn(new BigDecimalStringConverter()));
//Set how we want the cell to be edited including the row update.
column.setOnEditCommit(t -> {
handleEditCommit(t, field);
}); //END SetOnEditCommit.
} //END renderBigDecimalColumn
And my handleEditCommit method looks like:
/** handleEditCommit deals with updating and saving the new data from the table view.
*
* #param t
* #param field
*/
private void handleEditCommit(javafx.scene.control.TableColumn.CellEditEvent<AccountAsset,?> t, String field) {
AccountAsset rowData = t.getTableView().getItems().get(t.getTablePosition().getRow());
//Set the new value.
try {
BeanUtils.setProperty(rowData, field, t.getNewValue());
} catch (IllegalAccessException | InvocationTargetException ex) {
logger.error("handleEditCommit / Setter", ex);
}
//Save the new rowData back to the database.
try {
tableDataController.edit(rowData);
} catch (Exception ex) {
logger.error("handleEditCommit / Edit", ex);
}
}

Getting details from database Spring Boot , Exception error

I am trying to get data by multiple data from database on the basis of multiple Ids using Spring boot.
Basically it is a GET call which takes request parameters as a list of IDs and return response accordingly. IDs are unique in database
Url : api/details/1a,2a,3b
I am getting response as:
Get(value = "api/details/{Ids})
{
[id="1a",name="Raj", interest="Football"],
[id="2a",name="Tom", interest="Cricket"]
[id="3b",name="Kane", interest="Baseball"]
}
It is fine. But when i am giving a wrong Id, I am getting response as:
Url : api/details/xyz,abc,3b
{
null,
null,
[id="3b",name="Kane", interest="Baseball"]
}
I am expecting that instead of null it show say that the ID is not present along with Status code. Something like
{
2-Not found,3-Not Found,
id="3b",name="Kane", hobby="Baseball,
}
My controller class is like:
#RequestMapping(value = "api/details{Ids}", method = RequestMethod.GET, produces = MediaType.APPLICATION_JSON_VALUE)
public ResponseEntity<List<Game>>
getMultipleDetails(#PathVariable("Idss") String Idss) {
HttpHeaders headers = new HttpHeaders();
List<String> ids = Arrays.asList(Idss.split(","));
List<Game> list = new ArrayList<>();
Game details= null;
for (String id : ids) {
details= da.getMultipleDetails(id);
list.add(devices);
}
if (details== null) {
throw new RuntimeException(HttpStatus.NOT_FOUND.toString());
}
return new ResponseEntity<List<Game>>(list, headers, HttpStatus.OK);
}
}
My repository class is like:
public Device getMultipleDetails(String id) {
Game details= null;
try {
details= jdbcTemplate.queryForObject("SELECT * FROM table_name WHERE Id = ?",new DeviceRowMapper(), id);
} catch (Exception e) {
// Log the system generated Id
String systemRefId = String.valueOf(System.currentTimeMillis());
LOGGER.error(systemRefId, e);
//throw new DatabaseException(systemRefId, e);
}
return details;
}
Game is my model class that conatins id, name, hobby
As you're setting the ResponseEntity<List<Game>> you should only return a List with Game objects inside.
Not sure why you want to return the failed ones in the same List but as a workaround I will set id of the not found and, in the fields name and Game I will set 'Not found' instead of returning null objects. For example:
public Device getMultipleDetails(String id) {
Game details = new Game();
try {
details= jdbcTemplate.queryForObject("SELECT * FROM table_name WHERE Id = ?",new DeviceRowMapper(), id);
//If details is not null but it's empty
if (StringUtils.IsEmpty(details.getId())) {
details.setId(id);
details.setName("Not Found");
details.setGame("Not Found");
}
} catch (Exception e) {
// Log the system generated Id
String systemRefId = String.valueOf(System.currentTimeMillis());
LOGGER.error(systemRefId, e);
//If details is null it will trow null pointer exception
details = new Game();
details.setId(id);
details.setName("Not Found");
details.setGame("Not Found");
}
return details;
}
I strongly recommend you to rename the field Game in you Game class. A field should not duplicate the name of its containing class.
It's confusing to have a class member with the same name (case differences aside) as its enclosing class. This is particularly so when you consider the common practice of naming a class instance for the class itself.
Best practice dictates that any field or member with the same name as the enclosing class be renamed to be more descriptive of the particular aspect of the class it represents or holds.
I would recommend to rename it to something like typeOfGame for example.
You should manage the empty objects, and manage the message also, the code should be like this, because if not, only the last detail is the one evaluated, thats why the exception is not raised.
for (String id : ids) {
details= da.getMultipleDetails(id);
list.add(devices);
if (details== null) {
throw new RuntimeException(HttpStatus.NOT_FOUND.toString());
}
}

Cannot find classes in a package using Class.forName()

Using Java I am implementing a Page Factory object for selenium testing that takes the name of a page object and instantiates it through reflection for use by Cucumber step definitions. The problem I am having is that the code below cannot find the declared class. Both the object PageFactory which contains this code and the page object LoginPage reside in a package called pages.
/**
* This method take a string containing a Page Object class name (case-sensitive) and returns an instance of the Page Object.
* This allows us to operate on pages without knowing they exist when we write step definitions.
* #param choice String
* #return Page Object cast as a Page
*/
public static Page getPage(String choice) {
Page entity = null;
try {
entity = (Page) Class.forName(choice).newInstance();
} catch (InstantiationException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
return entity;
}
I receive a stack trace with java.lang.ClassNotFoundException: LoginPage as the beginning of the error. If I change the entity creation to the following code, then it works.
private static String packageName = "pages";
entity = (Page) Class.forName(packageName + "." + choice).newInstance();
The problem is that I want to organize my pages. When I create pages.mywebsite and place LoginPage within that, PageFactory won't know where to find the file.
Leaving aside the problem that I could have two namespaces pages.mywebsite and pages.myotherwebsite that both have a LoginPage object, how can I find the files I want without declaring the exact package, and just say "Look in this package and the ones below for the class"?
You could get the classpath using System.getProperty("java.class.path"), split it around File.pathSeparator, and scan the results using FileVisitor.
This is how I solved my problem. I moved the reflection into a findPageInPackage method, and the called it recursively to search through directories.
Full code here: https://github.com/dougnoel/sentinel/blob/master/src/main/java/com/dougnoel/sentinel/pages/PageFactory.java
/**
* Returns a page object if it exists in the package searched.
* #param pageName String the name of the page object class to instantiate
* #param packageName String the name of the package to search
* #return Page the page object if it exists, otherwise null
*/
private static Page findPageInPackage(String pageName, String packageName) {
Page page = null;
try {
page = (Page) Class.forName(packageName + "." + pageName).newInstance();
} catch (InstantiationException e) {
log.trace("{}.{} Page Object creation failed.", packageName, pageName);
log.trace("java.lang.InstantiationException: {}", e.getMessage());
} catch (IllegalAccessException e) {
log.trace("{}.{} Page Object creation failed.", packageName, pageName);
log.trace("java.lang.IllegalAccessException: {}", e.getMessage());
} catch (ClassNotFoundException e) {
log.trace("{}.{} Page Object creation failed.", packageName, pageName);
log.trace("java.lang.ClassNotFoundException: {}", e.getMessage());
}
return page;
}
/**
* Returns the Page Object for the page name. This allows us to operate on pages
* without knowing they exist when we write step definitions.
* <p>
* Searches any optional page object packages set with the pageObjectPackages system property, and
* then searches the defaultPackageName value.
* <p>
* <b>Example:</b>
* <p>
* <code>System.setProperty("pageObjectPackages", "pages.SharedComponent,pages.UserAdminTool");</code>
*
* #param pageName String the name of the page in
* <a href="https://en.wikipedia.org/wiki/Camel_case">Pascal
* case</a>
* #return Page the specific page object cast as a generic page object
* #throws PageNotFoundException if page could not be built or retrieved.
* #throws ConfigurationNotFoundException if the value is not found in the configuration file
*/
public static Page buildOrRetrievePage(String pageName) throws PageNotFoundException, ConfigurationNotFoundException {
Page page = pages.get(pageName);
final String errorMessage = "The page you want to test could not be built. At least one Page object package is required to run a test. Please add a pageObjectPackages property to your conf/sentinel.yml configuration file and try again.";
if (page != null) {
return page;
} else {
if (pageObjectPackagesList == null) {
pageObjectPackagesList = ConfigurationManager.getPageObjectPackageList();
if(pageObjectPackagesList == null) {
throw new PageNotFoundException(errorMessage);
}
}
for (String pageObjectPackage : pageObjectPackagesList) {
log.trace("pageObjectPackage: " + pageObjectPackage);
page = findPageInPackage(pageName, pageObjectPackage);
if (page != null) {
break; // If we have a page object, stop searching.
}
}
}
if(page == null) {
throw new PageNotFoundException(errorMessage);
}
pages.put(pageName, page);
return page;
}

Find and replace text in MS Access table rows not working

Given a directory, my application traverses and loads .mdb MS Access dbs using the Jackcess API. Inside of each database, there is a table named GCMT_CMT_PROPERTIES with a column named cmt_data containing some text. I also have a Mapper object (which essentially resembles a Map<String,String> but allows duplicate keys) which I use as a dictionary when replacing a certain word from a string.
So for example if mapper contains fox -> dog then the sentence: "The fox jumps" becomes "The dog jumps".
The design I'm going with for this program is as follows:
1. Given a directory, traverse all subdirectories and load all .mdb files into a File[].
2. For each db file in File[], create a Task<Void> called "TaskMdbUpdater" and pass it the db file.
3. Dispatch and run each task as it is created (see 2. above).
TaskMdbUpdater is responsible for locating the appropriate table and column in the db file it was given and iteratively running a "find & replace" routine on each row of the table to detect words from the dictionary and replace them (as shown in example above) and finally updating that row before closing the db. Each instance of TaskMdbUpdater is a background thread with a Jackcess API DatabaseBuilder assigned to it, so it is able to manipulate the db.
In the current state, the code is running without throwing any exceptions whatsoever, however when I "manually" open the db through Access and inspect a given row, it appears to not have changed. I've tried to pin the source of the issue without any luck and would appreciate any support. If you need to see more code, let me know and I'll update my question accordingly.
public class TaskDatabaseTaskDispatcher extends Task<Void> {
private String parentDir;
private String dbFileFormat;
private Mapper mapper;
public TaskDatabaseTaskDispatcher(String parent, String dbFileFormat, Mapper mapper) {
this.parentDir = parent;
this.dbFileFormat = dbFileFormat;
this.mapper = mapper;
}
#Override
protected Void call() throws Exception {
File[] childDirs = getOnlyDirectories(getDirectoryChildFiles(new File(this.parentDir)));
DatabaseBuilder[] dbs = loadDatabasesInParent(childDirs);
Controller.dprint("TaskDatabaseTaskDispatcher", dbs.length + " databases were found in parent directory");
TaskMdbUpdater[] tasks = new TaskMdbUpdater[dbs.length];
Thread[] workers = new Thread[dbs.length];
for(int i=0; i<dbs.length; i++) {
// for each db, dispatch Task so a worker can update that db.
tasks[i] = new TaskMdbUpdater(dbs[i], mapper);
workers[i] = new Thread(tasks[i]);
workers[i].setDaemon(true);
workers[i].start();
}
return null;
}
private DatabaseBuilder[] loadDatabasesInParent(File[] childDirs) throws IOException {
DatabaseBuilder[] dbs = new DatabaseBuilder[childDirs.length];
// Traverse children and load dbs[]
for(int i=0; i<childDirs.length; i++) {
File dbFile = FileUtils.getFileInDirectory(
childDirs[i].getCanonicalFile(),
childDirs[i].getName() + this.dbFileFormat);
dbs[i] = new DatabaseBuilder(dbFile);
}
return dbs;
}
}
// StringUtils class, utility methods
public class StringUtils {
public static String findAndReplace(String str, Mapper mapper) {
String updatedStr = str;
for(int i=0; i<mapper.getMappings().size(); i++) {
updatedStr = updatedStr.replaceAll(mapper.getMappings().get(i).getKey(), mapper.getMappings().get(i).getValue());
}
return updatedStr;
}
}
// FileUtils class, utility methods:
public class FileUtils {
/**
* Returns only directories in given File[].
* #param list
* #return
*/
public static File[] getOnlyDirectories(File[] list) throws IOException, NullPointerException {
List<File> filteredList = new ArrayList<>();
for(int i=0; i<list.length; i++) {
if(list[i].isDirectory()) {
filteredList.add(list[i]);
}
}
File[] correctSizeFilteredList = new File[filteredList.size()];
for(int i=0; i<filteredList.size(); i++) {
correctSizeFilteredList[i] = filteredList.get(i);
}
return correctSizeFilteredList;
}
/**
* Returns a File[] containing all children under specified parent file.
* #param parent
* #return
*/
public static File[] getDirectoryChildFiles(File parent) {
return parent.listFiles();
}
}
public class Mapper {
private List<aMap> mappings;
public Mapper(List<aMap> mappings) {
this.mappings = mappings;
}
/**
* Returns mapping dictionary, typically used for extracting individual mappings.
* #return List of type aMap
*/
public List<aMap> getMappings() {
return mappings;
}
public void setMappings(List<aMap> mappings) {
this.mappings = mappings;
}
}
/**
* Represents a single String based K -> V mapping.
*/
public class aMap {
private String[] mapping; // [0] - key, [1] - value
public aMap(String[] mapping) {
this.mapping = mapping;
}
public String getKey() {
return mapping[0];
}
public String getValue() {
return mapping[1];
}
public String[] getMapping() {
return mapping;
}
public void setMapping(String[] mapping) {
this.mapping = mapping;
}
}
Update 1:
To verify my custom StringUtils.findAndReplace logic, I've performed the following unit test (in JUnit) which is passing:
#Test
public void simpleReplacementTest() {
// Construct a test mapper/dictionary
List<aMap> aMaps = new ArrayList<aMap>();
aMaps.add(new aMap(new String[] {"fox", "dog"})); // {K, V} = K -> V
Mapper mapper = new Mapper(aMaps);
// Perform replacement
String corpus = "The fox jumps";
String updatedCorpus = StringUtils.findAndReplace(corpus, mapper);
assertEquals("The dog jumps", updatedCorpus);
}
I'm including my TaskMdbUpdater class here separately with some logging code included, as I suspect point of failure lies somewhere in call:
/**
* Updates a given .mdb database according to specifications defined internally.
* #since 2.2
*/
public class TaskMdbUpdater extends Task<Void> {
private final String TABLE_NAME = "GCMT_CMT_PROPERTIES";
private final String COLUMN_NAME = "cmt_data";
private DatabaseBuilder dbPackage;
private Mapper mapper;
public TaskMdbUpdater(DatabaseBuilder dbPack, Mapper mapper) {
super();
this.dbPackage = dbPack;
this.mapper = mapper;
}
#Override
protected Void call() {
try {
// Controller.dprint("TaskMdbUpdater", "Worker: " + Thread.currentThread().getName() + " running");
// Open db and extract Table
Database db = this.dbPackage
.open();
Logger.debug("Opened database: {}", db.getFile().getName());
Table table = db.getTable(TABLE_NAME);
Logger.debug("Opening table: {}", table.getName());
Iterator<Row> tableRows = table.iterator();
// Controller.dprint("TaskMdbUpdater", "Updating database: " + db.getFile().getName());
int i=0;
try {
while( tableRows.hasNext() ) {
// Row is basically a<code> Map<Column_Name, Value> </code>
Row cRow = tableRows.next();
Logger.trace("Current row: {}", cRow);
// Controller.dprint(Thread.currentThread().getName(), "Database name: " + db.getFile().getName());
// Controller.dprint("TaskMdbUpdater", "existing row: " + cRow.toString());
String str = cRow.getString(COLUMN_NAME);
Logger.trace("Row {} column field contents (before find/replace): {}", i, str);
String newStr = performFindAndReplaceOnString(str);
Logger.trace("Row {} column field contents (after find/replace): {}", i, newStr);
cRow.put(COLUMN_NAME, newStr);
Logger.debug("Updating field in row {}", i);
Row newRow = table.updateRow(cRow); // <code>updateRow</code> returns the new, updated row. Ignoring this.
Logger.debug("Calling updateRow on table with modified row");
// Controller.dprint("TaskMdbUpdater", "new row: " + newRow.toString());
i++;
Logger.trace("i = {}", i);
}
} catch(NoSuchElementException e) {
// e.printStackTrace();
Logger.error("Thread has iterated past number of rows in table", e);
}
Logger.info("Iterated through {} rows in table {}", i, table.getName());
db.close();
Logger.debug("Closing database: {}", db.getFile().getName());
} catch (Exception e) {
// e.printStackTrace();
Logger.error("An error occurred while attempting to update row value", e);
}
return null;
}
/**
* #see javafx.concurrent.Task#failed()
*/
#Override
protected void failed() {
super.failed();
Logger.error("Task failed");
}
#Override
protected void succeeded() {
Logger.debug("Task succeeded");
}
private String performFindAndReplaceOnString(String str) {
// Logger.trace("OLD: [" + str + "]");
String updatedStr = null;
for(int i=0; i<mapper.getMappings().size(); i++) {
// loop through all parameter names in mapper to search for in str.
updatedStr = findAndReplace(str, this.mapper);
}
// Logger.trace("NEW: [" + updatedStr + "]");
return updatedStr;
}
}
Here's a small exerept from my log. As you can see, it doesn't seem to do anything after opening the table which has left me a bit perplexed:
INFO (16-02-2017 17:27:59) [Thread-9] NAMEMAP.logic.TaskDatabaseTaskDispatcher.call(): Located the following directories under specified MOIS parent which contains an .mdb file:
[01_Parent_All_Safe_Test[ RV_DMS_0041RV_DMS_0001RV_DMS_0003RV_DMS_0005RV_DMS_0007RV_DMS_0012RV_DMS_0013RV_DMS_0014RV_DMS_0016RV_DMS_0017RV_DMS_0018RV_DMS_0020RV_DMS_0023RV_DMS_0025RV_DMS_0028RV_DMS_0029RV_DMS_0031RV_DMS_0033RV_DMS_0034RV_DMS_0035RV_DMS_0036RV_DMS_0038RV_DMS_0039RV_DMS_0040 ]]
...
DEBUG (16-02-2017 17:27:59) [Thread-9] NAMEMAP.logic.TaskDatabaseTaskDispatcher.call(): Created new task: NAMEMAP.logic.TaskMdbUpdater#4cfe46fe
DEBUG (16-02-2017 17:27:59) [Thread-9] NAMEMAP.logic.TaskDatabaseTaskDispatcher.call(): Created new worker: Thread[Thread-22,5,main]
DEBUG (16-02-2017 17:27:59) [Thread-9] NAMEMAP.logic.TaskDatabaseTaskDispatcher.call(): Set worker Thread[Thread-22,5,main] as daemon
DEBUG (16-02-2017 17:27:59) [Thread-9] NAMEMAP.logic.TaskDatabaseTaskDispatcher.call(): Dispatching worker: Thread[Thread-22,5,main]
...
DEBUG (16-02-2017 17:28:00) [Thread-22] NAMEMAP.logic.TaskMdbUpdater.call(): Opened database: RV_DMS_0023.mdb
DEBUG (16-02-2017 17:28:00) [Thread-22] NAMEMAP.logic.TaskMdbUpdater.call(): Opening table: GCMT_CMT_PROPERTIES
After this point, there isn't any more entries entries in the log and the processor spikes at 100% load, remaining that way until I force kill the application. This could mean the program gets stuck in an infinite while loop - however if that were to be the case then shouldn't there be log entries in the file?
Update 2
Okay I've further narrowed the problem by printing log TRACE into stdio. It seems that my performFindAndReplaceOnString is super inefficient and it never gets past the first row of these dbs because it's just grinding away at the long string. Any suggestions on how I can efficiently perform a string replacement for this use case?

Why is my setter called twice?

I am working on a REST web service, using JAX-RS, JPA and JAXB, for the management of games and their highscores. A game has the following properties: name, url and highscoreTableSize.
A short description of what I'm trying to do: I have the createRow() method in the controller which consumes JSON (the JSON serialization of a Game object, class Game being annotated with #XmlRootElement), which calls the static createRow() from the Game model class, and inside of it the setUrl() is called. The thing is that, for some reason, the setter is called twice.
Now what it happens is that, if the url sent in the body of the request is not valid against a pattern, after the "mysterious" first call it becomes null, and the second time the setter is called, it goes inside if (url == null), instead of going inside if (!matcher.matches()), when actually the latter is the real situation, because I've sent a mistyped URL.
Does anybody know why this is happening and how can I solve this?
Thank you in advance!
Class Game:
#Entity
#Table(name="games")
#XmlRootElement(name = "Game")
public class Game implements Serializable {
//properties
public void setUrl(String url) throws CustomWebServiceException {
String regex = "^(https?|ftp|file)://[-a-zA-Z0-9+&##/%?=~_|!:,.;]*[-a-zA-Z0-9+&##/%=~_|]";
Pattern pattern = Pattern.compile(regex);
System.out.println("URL: " + url);
if ( url == null || url.length() == 0) {
throw new CustomWebServiceException(Response.Status.BAD_REQUEST, new ErrorMessage("The url of the game is mandatory!"));
} else {
Matcher matcher = pattern.matcher(url);
if (!matcher.matches()) {
throw new CustomWebServiceException(Response.Status.BAD_REQUEST, new ErrorMessage("The url is invalid! Please check its syntax!"));
} else {
this.url = url;
}
}
}
public static Response createRow(EntityManager em, UserTransaction ut, String name, Game gameData) throws Exception {
ut.begin();
Game _game = em.find(Game.class, name);
if (_game != null) {
Util.tryRollback(ut);
ErrorMessage errorMessage = new ErrorMessage(
"The game with name " + name
+ " already exists in the database!");
throw new CustomWebServiceException(Response.Status.CONFLICT,
errorMessage);
}
String url = gameData.getUrl();
Integer highscoreTableSize = gameData.getHighscoreTableSize();
Game newGame = new Game();
newGame.setName(name);
newGame.setUrl(url);
newGame.setHighscoreTableSize(highscoreTableSize);
em.persist(newGame);
// force the persistence manager to save data to DB
ut.commit();
if (highscoreTableSize == null) {
highscoreTableSize = 7;
}
SuccessfulRequestMessage succesfulRequestMessage = new SuccessfulRequestMessage(
" Game entry created with name: " + name
+ ", url: " + url + " and highscoreTableSize: " + highscoreTableSize
+ ".");
return Response.status(Status.CREATED).entity(succesfulRequestMessage).type(MediaType.APPLICATION_JSON).build();
}
}
Controller:
#PUT
#Path("/{name}")
#Consumes(MediaType.APPLICATION_JSON)
#Produces(MediaType.APPLICATION_JSON)
public Response createRow(
#PathParam("name") String name,
Game gameData) throws CustomWebServiceException {
try {
return Game.createRow(em, ut, name, gameData);
} catch (SystemException | NotSupportedException | IllegalStateException | SecurityException | HeuristicMixedException
| HeuristicRollbackException | RollbackException e) {
Util.tryRollback(ut);
ErrorMessage errorMessage = new ErrorMessage(
"Error when trying to create entry:" + e.toString()
+ " with message: " + e.getMessage());
throw new CustomWebServiceException(
Response.Status.INTERNAL_SERVER_ERROR, errorMessage);
} catch (CustomWebServiceException e) {
throw e;
} catch (Exception e) {
Util.tryRollback(ut);
ErrorMessage errorMessage = new ErrorMessage(
"During creation of game data, the following error(s) was(were) encountered: "
+ e.toString());
throw new CustomWebServiceException(Response.Status.BAD_REQUEST,
errorMessage);
}
}
Well, it should be called twice as per your code. Once during deserialization and once you do it yourself:
newGame.setUrl(url);
Using the same class for model and for representation is a bad idea in general.
IMHO,What you should do:
Separate your "JSON" game from the object you save in the database
Don't do validation in your setters. There is a Spring Validation for that. Use that to make sure that your JSON object is valid and then just go directly for the database.
You can use dozer to automatically convert model object to representation objects and vice versa
Edit:
Without using any libraries the easiest thing you can do is to move validation to a method in your controller:
void validateInput(Game game) throws Exception {
if (game == null) {
throw new Exception("Game object is not present in the request");
}
if (game.getUrl() == null || !game.maches({some-fancyreg-exp}) {
throw new Exception("Game URL is not valid");
}
//etc, check the rest of the fields
}
Call validateInput(game) in your controller. After that you can be sure that the input is valid. Quick and dirty. Let setters be setters.

Categories

Resources