cancel
Showing results for 
Search instead for 
Did you mean: 
cancel
Showing results for 
Search instead for 
Did you mean: 

Community Tip - Need help navigating or using the PTC Community? Contact the community team. X

Creating Custom Services on top of a DataTable created in composer using the Eclipse SDK

Raccone
12-Amethyst

Creating Custom Services on top of a DataTable created in composer using the Eclipse SDK

Dear community,

 

I would like to perform the activity I have written in the subject, and by looking at the documentation it looks doable. However, I have some issues:

  1. I have created a DataShape and a DataTable in the composer, and exported them via the "Export" option.
  2. I have then imported them in Eclipse (Right click on Extension Project -> Import -> ThingWorx -> Entities and the .xml files are imported correctly in the project.

After this, I am not sure on how to proceed, since the import option does not cause the creation of the corresponding Java classes, or the entries in the metadata.xml file. By following tutorials/documentation, custom services should be created in the .java class of the related entity. Should I perform those manually, and then map them to the entities I created? 

I hope my question is clear, but feel free to ask for more details.

Federico

ACCEPTED SOLUTION

Accepted Solutions
VladimirRosu
19-Tanzanite
(To:Raccone)

Hi Federico,

 

I don't believe you necessarily need this part, since you will assign it anyway below.

= new DataTableThing()

 Regarding your questions:

1. Is there a way to override the http status of the response?

-I haven't tried to do this, and typically at the SDK level I expect you would not be able to do that.

2. Is there a suggested best practice to develop multiple service for the single entity, e.g., build many Services in the main Java class or having a Service per Java class?

-typically you would want multiple services per ThingShape class, but you should follow architectural separation rules if your application uses more than one tier, and in that case you might need to separate the services in different classes that you attach to different Things maybe. The ThingShape is theoretically supposed to offer reusability, but also, like in your case, to offer opposite language capabilities to the entity you attach it to. For example, in the GitBackup Extension I have a Java ThingTemplate in which I do only the operations I can not do in Javascript. For those operations I have a JavaScript ThingShape that I attach to my Things.

3. Is there a suggested way to interact between different DataTables, e.g., joining two tables?

Yes, that is to use the 34 snippets dedicated to infotable interaction from the Service editor in Composer

 

 

View solution in original post

10 REPLIES 10
VladimirRosu
19-Tanzanite
(To:Raccone)

What is the use-case you're trying to achieve here? I would suggest adding normal ThingWorx Composer services directly at the level of the DataTable, but maybe you're aiming for something that can't be done via this method?

 

The import process you used adds entities to a ThingWorx Extension Project with the aim to package them in extension zip file and nothing else.

The reason to do this is because extensions in some cases must contain entities that do not make sense to be created via code because of flexibility and speed reasons and instead it just embeds them in the Entities folder that will be automatically imported once you load your extension.

To be clear, such entities will never generate Java classes.

To achieve your usecase, declare a ThingShape, write your own services in the Java, then add it to an existing DataTable.

One additional option, but which I never tested and I'm not sure it will work, is to generate a Template that inherits the DataTable thing, and then manually change the class it extends - which should be Thing - to DataTableThing.

 

 

Hello Vladmir,

 

I'd like to use the Eclipse SDK because the business logic behind the Service could become rather complex, and I have seen in the docs that Services written in JS using the composed cannot be debugged.

 

If I understand your first option, I can write the Services in a ThingShape class, then deploy the extension to my instance and subsequently derive the DataTable thing from the ThingShape I have deployed?

 

Thanks,

Federico

 

Edit: I wrongly put DataShape in lieu of ThingShape

VladimirRosu
19-Tanzanite
(To:Raccone)

Yes, that's almost correct because you can't derive the DataTable from a ThingShape, but instead you add a ThingShape to a DataTable. The ThingShape is almost like a Java abstract class.

 

Gotcha regarding the usage of Eclipse SDK, but I would advise that keep in mind that when you code in Java you need to recompile, deploy + a platform restart that is required to reload the classes. With all the lack of debugger and the resulting workarounds (write a logger.warn will become your friend), it is still faster to change the JS code. Another point is of maintainability: if you code in JS, then you have immediate access to that code in Composer, which will help with future maintainability - I mean there's no chance to forget to send or loose the source code with this approach. I can also mention the hundreds of snippets available for consumption directly in the service editor.

 

So my best advice here is really to time how effective you are in both approaches and then choose one that makes sense.

Hi @VladimirRosu,

 

Thanks for your answer, it makes a lot of sense to me now. I would still want to try and build a small "poc like" example in Java and see how it feels, but the JS alternative looks good too.

 

I have just tried to create a ThingShape class, but I have seen that it just comes with an empty constructor. I have tried to customize it as follows:

public class TestUtServices {

	private static Logger _logger = LogUtilities.getInstance().getApplicationLogger(TestUtServices.class);

	private DataTableThing usersTable = (DataTableThing) ThingUtilities.findThing("SomeTable");
	
	public TestUtServices() {
		// TODO Auto-generated constructor stub
	}

	@ThingworxServiceDefinition(name = "ReadDataCustomService", description = "Custom Service to read Table Data", category = "", isAllowOverride = false, aspects = {
			"isAsync:false" })
	@ThingworxServiceResult(name = "Data Items", description = "", baseType = "JSON", aspects = {})
	public JSONObject ReadDataCustomService() {
		_logger.trace("Entering Service: ReadDataCustomService");
		
		InfoTable resultsTable = new InfoTable();
		
		try {
			resultsTable = usersTable.GetDataTableEntries((double) 100);
		} catch (Exception e) {
			e.printStackTrace();
			_logger.error(e.getMessage());
		}
		
		JSONObject queryResults = new JSONObject();
		
		try {
			queryResults = resultsTable.toJSON();
		} catch (Exception e) {
			e.printStackTrace();
			_logger.error(e.getMessage());
		}
		
		_logger.trace("Exiting Service: ReadDataCustomService");
		return queryResults;
	}
}


Assuming that my approach makes sense in general, that would require the table I refer to in ThingUtilities.findThing to exist already. Also, it's not very clear to me how the InfoTable should map to a DataShape.

 

Thanks for your support,

Federico

VladimirRosu
19-Tanzanite
(To:Raccone)

Very good progress.

The DataTable does not need to be defined ahead of time, you need to get the "me" context to which your ThingShape is attached to. There's an article about this: https://www.ptc.com/en/support/article/CS226689

 

The InfoTable is a basetype (or primitive type) and the DataShape is a collection of column header definitions, each including the column name and basetype (at least). Infotable contains values, DataShape does not.

The DataShape is generally needed to let the MashupBuilder know what kind of data it should expect from a service - so you can do all the bindings.

Thanks @VladimirRosu,

 

Regarding the first point, I will try to use it. If I am not wrong that should help avoiding direct references to a particular Thing as I made in my code snippet.

 

Regarding the InfoTable, if I understand you correctly, my snippet should work because, since it is a primitive type, it will just be filled with rows as defined in the DataShape from which the DataTable is derived? 

 

 

 

VladimirRosu
19-Tanzanite
(To:Raccone)

Correct.

Watch out in the future, when you want to create an Infotable primitive Type on your own, because in Java they require a different way of adding rows, you need to create a ValueCollection first - which is the row object, which you fill with key:value, and then you add it to the Infotable object. There should be documentation somewhere about this.

 

Hi @VladimirRosu,

 

I have corrected the simple example that I have posted before, and I'm leaving the code here as a reference for anyone who desires to implement a similar scenario:

 

 

public class TestUtServices {

	private static Logger _logger = LogUtilities.getInstance().getApplicationLogger(TestUtServices.class);

	private DataTableThing usersTable = new DataTableThing();
	
	public TestUtServices() {
		// TODO Auto-generated constructor stub
	}

	@ThingworxServiceDefinition(name = "ReadDataCustomService", description = "Custom Service to read Table Data", category = "", isAllowOverride = false, aspects = {
			"isAsync:false" })
	@ThingworxServiceResult(name = "Data Items", description = "", baseType = "JSON", aspects = {})
	public JSONObject ReadDataCustomService() {
		_logger.trace("Entering Service: ReadDataCustomService");
		
		Object me = ThreadLocalContext.getMeContext();
		
		if (me instanceof DataTableThing) {
			usersTable = (DataTableThing) me;
		} else {
			return new JSONObject();
		}
		
		InfoTable resultsTable = new InfoTable();
		
		try {
			resultsTable = usersTable.GetDataTableEntries((double) 100);
		} catch (Exception e) {
			e.printStackTrace();
			_logger.error(e.getMessage());
		}
		
		JSONObject queryResults = new JSONObject();
		
		try {
			queryResults = resultsTable.toJSON();
		} catch (Exception e) {
			e.printStackTrace();
			_logger.error(e.getMessage());
		}
		
		_logger.trace("Exiting Service: ReadDataCustomService");
		return queryResults;
	}
}

 

 

This worked like a charm as I was able to import it in the Composer, adding it to the already existing DataTable, and testing it via the "Execute" button. There are a few things I want to ask (I swear, the last :)):

  1. Is there a suggested way to implement more services, i.e., using a ThingShape per Service, or the same ThingShape per different services?
  2. I don't quite understand how I should get/construct the URL to access the service remotely (for example, via Postman)
  3. Are there suggested ways to implement cross-tables services, e.g., making a join between two tables?

 

Thank you very much!

Federico 

Hi @VladimirRosu,

 

Thanks for your explanation, it helped me and I could also deploy my first custom Service successfully. I paste here the code since it can serve as a future reference:

public class TestUtServices {

	private static Logger _logger = LogUtilities.getInstance().getApplicationLogger(TestUtServices.class);

	private DataTableThing usersTable = new DataTableThing();
	
	public TestUtServices() {
		// TODO Auto-generated constructor stub
	}

	@ThingworxServiceDefinition(name = "ReadDataCustomService", description = "Custom Service to read Table Data", category = "", isAllowOverride = false, aspects = {
			"isAsync:false" })
	@ThingworxServiceResult(name = "Data Items", description = "", baseType = "JSON", aspects = {})
	public JSONObject ReadDataCustomService() {
		_logger.trace("Entering Service: ReadDataCustomService");
		
		Object me = ThreadLocalContext.getMeContext();
		
		if (me instanceof DataTableThing) {
			usersTable = (DataTableThing) me;
		} else {
			return new JSONObject();
		}
		
		InfoTable resultsTable = new InfoTable();
		
		try {
			resultsTable = usersTable.GetDataTableEntries((double) 100);
		} catch (Exception e) {
			e.printStackTrace();
			_logger.error(e.getMessage());
		}
		
		JSONObject queryResults = new JSONObject();
		
		try {
			queryResults = resultsTable.toJSON();
		} catch (Exception e) {
			e.printStackTrace();
			_logger.error(e.getMessage());
		}
		
		_logger.trace("Exiting Service: ReadDataCustomService");
		return queryResults;
	}
}

 So I have a few more questions: 

Is there a way to override the http status of the response?

Is there a suggested best practice to develop multiple service for the single entity, e.g., build many Services in the main Java class or having a Service per Java class?

Is there a suggested way to interact between different DataTables, e.g., joining two tables?

 

Thanks,

Federico

VladimirRosu
19-Tanzanite
(To:Raccone)

Hi Federico,

 

I don't believe you necessarily need this part, since you will assign it anyway below.

= new DataTableThing()

 Regarding your questions:

1. Is there a way to override the http status of the response?

-I haven't tried to do this, and typically at the SDK level I expect you would not be able to do that.

2. Is there a suggested best practice to develop multiple service for the single entity, e.g., build many Services in the main Java class or having a Service per Java class?

-typically you would want multiple services per ThingShape class, but you should follow architectural separation rules if your application uses more than one tier, and in that case you might need to separate the services in different classes that you attach to different Things maybe. The ThingShape is theoretically supposed to offer reusability, but also, like in your case, to offer opposite language capabilities to the entity you attach it to. For example, in the GitBackup Extension I have a Java ThingTemplate in which I do only the operations I can not do in Javascript. For those operations I have a JavaScript ThingShape that I attach to my Things.

3. Is there a suggested way to interact between different DataTables, e.g., joining two tables?

Yes, that is to use the 34 snippets dedicated to infotable interaction from the Service editor in Composer

 

 

Announcements


Top Tags