Announcement

Collapse
No announcement yet.
X
  • Filter
  • Time
Clear All
new posts

    Custom DataSource

    Hi,

    I need to implement a DataSource that's backed by Spring+Hibernate to process a form. However, the dataSource doesn't simply map the fields of a form to a Hibernate POJO (as in the typical DataSource examples).

    Instead, the form acts more like a "wizard" - users enter data and based on that data a number of different POJO's will be created and persisted to Hibernate.

    The dataSource then has to supply a response back that indicates which POJO's were successfully created, which already existed and would have resulted in a duplicate; and which simply failed (due to a Hibernate error or some other sort of problem). That data is then displayed to the user.

    From looking at the API docs and the showcase (specifically, the ORM DataSource example in the Enterprise Showcase); I imagine I will have to create a Custom DataSource implementation on the Server side, by extending com.isomorphic.datasource.BasicDataSource; as well as a client DataSource implementation that extends com.smartgwt.client.data.DataSource with a transformHandler to process the response.

    However, some things aren't very clear to me and I was hoping for some pointers that could get me on my way.

    1) I've looked at the documentation for the server side APIs but there's no javadoc on the BasicDataSource class, so I'm not sure what exactly to extend on that class. The ORM DataSource example shows a simple CRUD implementation, but in this case I don't need a CRUD design pattern. Should I just implement BasicDataSource.add(Object arg0)?

    2) The ORM Datasource example uses an XML-mapped Datasource (cfr. the file ormDataSource_country.ds.xml) but I'm not sure that this will work for me. Don't dataSource declared this way need to have a primary key? The problem is that since my form is purely for data-entry and not backed by an actual POJO, the concept of a PK makes no sense here.

    3) From the javadoc on com.smartgwt.client.data.DataSource, I gather that you need to call setDataURL to tell the client DS where the server DS is. However, all the examples I've seen in the showcase of custom client DataSources use test data and are client-only so I can't figure out what actual value to set this to. Any pointers regarding this?

    4) Finally, I don't really care about whether I submit the data as XML or JSON, since I'll be writing both the client and server side of this solution. Something that would allow me to easily unmarshal a form and marshal it again on the server side would probably be best. Any ideas on this?

    Thanks!

    #2
    Ok, after looking at this some more, I've realized that I need to implement executeCustom(DSRequest dsRequest) on the server DataSource. However, it's not clear to me how to tell my client Datasource to invoke the custom operation on the datasource - import com.smartgwt.client.data.DataSource has a setOperationBinding method, and OperationBindings have operationTypes, but the only possible types are FETCH, ADD, UPDATE and REMOVE. How do you provide a CUSTOM operationBinding?
    Last edited by SiccoNaets; 9 Jul 2010, 11:56.

    Comment


      #3
      You can use an "add" operation in this case without a PK field.

      First question is whether any subset of the data describes an entity that will also be manipulated elsewhere in the application. If so, it's probably worthwhile to make a DataSource for that to share the data definition.

      If you end up with, say, two sets of fields that describe DataSources used elsewhere in the app, and a mishmash of other field definitions you won't reuse, make two normal DataSources and throw the extra field definitions into a third DataSource and save via 3 "add" operations in a queue (RPCManager.startQueue()).

      For the third DataSource implement a DMI for the "add" operation and save however you're saving (which is how, by the way? You're aware there's a non-POJO, non-Hibernate SQLDataSource too, right?).

      In no case do you need to make a parallel client-side DataSource. Just use the DataSourceLoader as shown in all Pro/EE samples. No need to set dataURL unless you have changed the default location of the IDACall servlet. No need to select a dataFormat, it uses the ISCServer dataFormat automatically.

      Comment


        #4
        Thanks for the reply Isomorphic.

        The specific objects that this form will be creating are sets of phone-numbers. The form allows the specification of phone number ranges (for instance, all numbers from 1-202-204-0001 to 1-202-204-9999). I'm just mentioning this because it might make what follows a little easier to understand.

        In addition to the basic number information, we're also adding extra information (i.e. what carrier these phone numbers are with, what account they are assigned to, and so forth).

        Here is the what the form looks like, just as an FYI:

        http://yfrog.com/juadddidformp

        I've created a DataSource implementation that only implements the add(DSRequest dsRequest) method. Inside the add() method, I'm processing the values from the dsRequest and loading them into a throw-away POJO (didWizardData) so that I can easily see what was entered into the form.

        I then figure out what the range of phone numbers is, and instantiate a new POJO for each phone number to create. I then persist these phone-number-pojos to hibernate one by one. As I do this, I populate three lists: the ones that were succesfully added, the ones that already exist, and the ones that generated an error during the creation process. I wrap those three lists into another throw-away object and then call DSResponse.setData() to give that back to the application.

        My plan was to add an asynchronous callback to the saveData() call of the form and then display a window showing the contents of the three result lists; so that the users have some idea of what just happened.

        Anyway, the dataSource is written. However, the form I've showed you is actually multiple forms backed by a valueManager (for layout purposes). When I call saveData() on the ValueManager, for some reason it sends an update request to the server; rather than an add request and I get the following error:

        Code:
        17:19:44,946  WARN RequestContext:369 - dsRequest.execute() failed: 
        com.isomorphic.base.UpdateWithoutPKException: update operation received from client for DataSource 'didWizard', operatio
        nId 'didWizard_update'. This is not allowed because the DataSource has no primaryKey.  Either declare a primaryKey or se
        t allowMultiUpdate to true on the OperationBinding
        	at com.isomorphic.application.AppBase.executeDefaultDSOperation(AppBase.java:711)
        	at com.isomorphic.application.AppBase.executeAppOperation(AppBase.java:658)
        	at com.isomorphic.application.AppBase.execute(AppBase.java:491)
        	at com.isomorphic.datasource.DSRequest.execute(DSRequest.java:1250)
        	at com.isomorphic.servlet.IDACall.handleDSRequest(IDACall.java:155)
        	at com.isomorphic.servlet.IDACall.processRequest(IDACall.java:106)
        	at com.isomorphic.servlet.IDACall.doPost(IDACall.java:54)
        	at javax.servlet.http.HttpServlet.service(HttpServlet.java:637)
        	at com.isomorphic.servlet.BaseServlet.service(BaseServlet.java:152)
        	at javax.servlet.http.HttpServlet.service(HttpServlet.java:717)
        	at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:290)
        	at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:206)
        	at org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:233)
        	at org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:191)
        	at org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:127)
        	at org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:102)
        	at org.apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve.java:109)
        	at org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:298)
        	at org.apache.coyote.http11.Http11Processor.process(Http11Processor.java:852)
        	at org.apache.coyote.http11.Http11Protocol$Http11ConnectionHandler.process(Http11Protocol.java:588)
        	at org.apache.tomcat.util.net.JIoEndpoint$Worker.run(JIoEndpoint.java:489)
        	at java.lang.Thread.run(Thread.java:619)
        Here is the source code for the datasource:

        Code:
        <DataSource
        	ID="didWizard"
        	serverType="generic">
        		<fields>
        			<field name="countryDialCode" type="int"/>
        			<field name="npa" type="text"/>
        			<field name="nxx" type="text"/>
        			<field name="extensionFrom" type="text"/>
        			<field name="extensionTo" type="text"/>
        			<field name="carrierId" type="int"/>
        			<field name="portStatus" type="text"/>
        			<field name="accountId" type="text"/>
        			<field name="csaId" type="text"/>
        			<field name="entryMethod" type="enum" required="yes"/>
        			<field name="manualEntry" type="text"/>
        			<field name="notes" type="text"/>
        		</fields>
        		
        	
        	 	<serverObject lookupStyle="spring" bean="didWizardDAO"/>
        	
        </DataSource>
        Code for the Form:

        Code:
        public class WindowedDIDWizardView extends Window implements View
        {
        	WindowedDIDWizardController controller;
        	
        	protected DataSource dataSource;
        	protected DataSource countryDS;
        	protected DataSource carrierDS;
        	protected DataSource didPortStatusDS;
        	protected DataSource accountDS;
        	protected DataSource csaDS;
        	
        	
        	protected VStack layout;
        	
        	protected HStack formLayout;
        	protected VStack notesLayout;
        	
        	protected DynamicForm entryMethodForm;
        	protected DynamicForm leftRangeEntryForm;
        	protected DynamicForm leftManualEntryForm;
        	protected DynamicForm rightForm;
        	protected DynamicForm buttonForm;
        	protected ValuesManager vm;
        	
        	
        	protected RadioGroupItem entryMethod;
        	protected ComboBoxItem countryCode;
        	protected TextItem npa;
        	protected TextItem nxx;
        	protected TextItem extensionFrom;
        	protected TextItem extensionTo;
        	protected TextAreaItem manualEntry;
        	protected ComboBoxItem carrier;
        	protected ComboBoxItem portStatus;
        	protected ComboBoxItem account;
        	protected ComboBoxItem csa;
        	protected RichTextEditor notes;
        	
        	protected ButtonItem createButton;
        	protected ButtonItem resetButton;
        	
        	public WindowedDIDWizardView(HandlerManager eventBus, ListGrid target)
        	{
        		super();
        		
        		dataSource = DataSource.get("didWizard");
        		countryDS = DataSource.get("country");
        		carrierDS = DataSource.get("carrier");
        		didPortStatusDS = DataSource.get("didPortStatus");
        		accountDS = DataSource.get("account");
        		csaDS = DataSource.get("csa");
        		
        		/* Create the controller */
        		controller = new WindowedDIDWizardController(eventBus, target);
        		
        		
        		this.setAutoSize(true);
        		this.setShowEdges(false);
        		this.setStyleName("didWindow");
        		this.setHeaderStyle("didWindowHeader");
        		this.setShowShadow(true);
        		this.setShadowDepth(10);
        		this.setVisibility(Visibility.HIDDEN);
        		this.setCanDragReposition(false);
        		this.setCanDragResize(false);
        		this.setTitle("Add DIDs");
        		this.setHeaderControls(HeaderControls.HEADER_LABEL, HeaderControls.CLOSE_BUTTON);
        		
        		layout = new VStack();
        		layout.setAlign(Alignment.CENTER);
        		
        		
        		formLayout= new HStack();
        		formLayout.setPadding(10);
        		formLayout.setMembersMargin(5);
        	
        		
        		notesLayout = new VStack();
        		
        		entryMethodForm = new DIDWizardForm();
        		entryMethodForm.setStyleName("entryMethodForm");
        		
        		
        		
        		leftRangeEntryForm = new DynamicForm();
        		leftManualEntryForm = new DynamicForm();
        		rightForm = new DIDWizardForm();
        		buttonForm =new DynamicForm();
        		
        		leftRangeEntryForm.setDataSource(dataSource);
        		leftManualEntryForm.setDataSource(dataSource);
        		rightForm.setDataSource(dataSource);
        		
        		vm = new ValuesManager();
        		vm.setDataSource(dataSource);
        		
        		leftRangeEntryForm.setValuesManager(vm);
        		leftManualEntryForm.setValuesManager(vm);
        		rightForm.setValuesManager(vm);
        	
        		entryMethodForm.setNumCols(4);
        		leftRangeEntryForm.setNumCols(4);
        		
        		leftManualEntryForm.setVisibility(Visibility.HIDDEN);
        		buttonForm.setColWidths(500, 500);
        		buttonForm.setFixedColWidths(true);
        		
        		entryMethod = new RadioGroupItem(DIDWizardField.ENTRY_METHOD.getName());
        		entryMethod.setTitle(DIDWizardField.ENTRY_METHOD.getDisplayTitle());
        		entryMethod.setValueMap(EntryMethod.getValueMap());
        		entryMethod.setVertical(false);
        		entryMethod.setWrap(false);
        		entryMethod.setDefaultValue(EntryMethod.RANGE_ENTRY.getName());
        		
        		countryCode=new ComboBoxItem(DIDWizardField.COUNTRY_DIAL_CODE.getName());
        		countryCode.setTitle(DIDWizardField.COUNTRY_DIAL_CODE.getDisplayTitle());
        		countryCode.setColSpan(4);
        		countryCode.setOptionDataSource(countryDS);
        		countryCode.setValueField(CountryField.COUNTRY_ID.getName());
        		countryCode.setDisplayField(CountryField.COUNTRY_NAME.getName());
        		countryCode.setOptionOperationId("filtered");
        		countryCode.setAutoFetchData(true);
        		
        		ListGridField countryFlag = new ListGridField(CountryField.COUNTRY_CODE.getName(), "Flag", 30);
        		countryFlag.setType(ListGridFieldType.IMAGE);
        		countryFlag.setImageURLPrefix("flags/16/");  
        		countryFlag.setImageURLSuffix(".png");
        		
        		ListGridField countryName = new ListGridField(CountryField.COUNTRY_NAME.getName(), CountryField.COUNTRY_NAME.getDisplayTitle());
        		countryCode.setPickListFields(countryFlag, countryName);
        		
        		npa = new TextItem(DIDWizardField.NPA.getName());
        		npa.setTitle(DIDWizardField.NPA.getDisplayTitle());
        		npa.setWidth(50);
        		
        		nxx = new TextItem(DIDWizardField.NXX.getName());
        		nxx.setTitle(DIDWizardField.NXX.getDisplayTitle());
        		nxx.setWidth(50);
        		
        		extensionFrom = new TextItem(DIDWizardField.EXTENSION_FROM.getName());
        		extensionFrom.setTitle(DIDWizardField.EXTENSION_FROM.getDisplayTitle());
        		extensionFrom.setWidth(50);
        		
        		extensionTo = new TextItem(DIDWizardField.EXTENSION_TO.getName());
        		extensionTo.setTitle(DIDWizardField.EXTENSION_TO.getDisplayTitle());
        		extensionTo.setWidth(50);
        		
        		manualEntry = new TextAreaItem(DIDWizardField.MANUAL_ENTRY.getName());
        		manualEntry.setTitle(DIDWizardField.MANUAL_ENTRY.getDisplayTitle());
        		manualEntry.setTitleVAlign(VerticalAlignment.TOP);
        		manualEntry.setWidth(166);
        		
        		carrier = new ComboBoxItem(DIDWizardField.CARRIER.getName());
        		carrier.setTitle(DIDWizardField.CARRIER.getDisplayTitle());
        		carrier.setOptionDataSource(carrierDS);
        		carrier.setValueField(CarrierField.PROVIDER_ID.getName());
        		carrier.setDisplayField(CarrierField.PROVIDER_NAME.getName());
        	
        		
        		portStatus = new ComboBoxItem(DIDWizardField.PORT_STATUS.getName());
        		portStatus.setTitle(DIDWizardField.PORT_STATUS.getDisplayTitle());
        		portStatus.setOptionDataSource(didPortStatusDS);
        		
        		account = new ComboBoxItem(DIDWizardField.ACCOUNT.getName());
        		account.setTitle(DIDWizardField.ACCOUNT.getDisplayTitle());
        		account.setOptionDataSource(accountDS);
        		account.setValueField(AccountField.ACCOUNT_ID.getName());
        		account.setDisplayField(AccountField.ACCOUNT_NAME.getName());
        		
        		csa= new ComboBoxItem(DIDWizardField.CSA.getName());
        		csa.setTitle(DIDWizardField.CSA.getDisplayTitle());
        		csa.setOptionDataSource(csaDS);
        		csa.setValueField(CSAField.CSA_ID.getName());
        		csa.setDisplayField(CSAField.CSA.getName());
        		
        		notes = new RichTextEditor();
        		notes.setHeight(125);
        		notes.setShowEdges(true);
        		notes.setWidth("550");
        		notes.setOverflow(Overflow.HIDDEN);
        		
        		createButton = new ButtonItem("Create");
        		createButton.setEndRow(false);
        		createButton.setAlign(Alignment.RIGHT);
        		resetButton = new ButtonItem("Reset");
        		resetButton.setStartRow(false);
        		
        		entryMethodForm.setFields(entryMethod);
        		leftRangeEntryForm.setFields(countryCode, npa, nxx, extensionFrom, extensionTo);
        		leftManualEntryForm.setFields(manualEntry);
        		
        		rightForm.setFields(carrier,portStatus,account);
        		
        		DIDSearchFormDecorator.decorate(entryMethodForm, leftRangeEntryForm, leftManualEntryForm, rightForm);
        		DIDSearchFormDecorator.decorate(entryMethod, countryCode, npa, nxx, extensionFrom, extensionTo, manualEntry, carrier, portStatus, account, csa);
        		
        		Label notesLabel = new Label();
        		notesLabel.setText(DIDWizardField.NOTES.getDisplayTitle());
        		notesLabel.setHeight("10");
        		notesLayout.addMember(notesLabel);
        		notesLayout.addMember(notes);
        		
        		formLayout.addMember(leftRangeEntryForm);
        		formLayout.addMember(leftManualEntryForm);
        		formLayout.addMember(rightForm);
        		
        		
        		formLayout.addMember(notesLayout);
        		
        		buttonForm.setFields(createButton, resetButton);
        		
        		layout.addMember(entryMethodForm);
        		layout.addMember(formLayout);
        		layout.addMember(buttonForm);
        		
        		this.addItem(layout);
        		
        	}
        	
        	@Override
        	public void bind() {
        		controller.bind();
        	}
        
        
        
        	@Override
        	public void init() {
        		controller.init();
        		
        	}
        
        
        
        	@Override
        	public void unbind() {
        		controller.unbind();
        	}
        	
        	@Override
        	public void destroy()
        	{
        		controller.destroy();
        		super.destroy();
        	}
        	
        	public void setRangeEntryFormVisbility(Visibility visibility)
        	{
        		this.leftRangeEntryForm.setVisibility(visibility);
        	}
        	
        	public void setManualEntryFormVisibility(Visibility visibility)
        	{
        		this.leftManualEntryForm.setVisibility(visibility);
        	}
        	
        	
        	public class DIDWizardForm extends DynamicForm
        	{
        		@Override
        		public void saveData()
        		{
        			this.setValue(DIDWizardField.NOTES.getName(), notes.getValue());
        			super.saveData();
        		}
        	}
        
        	public class WindowedDIDWizardController extends AbstractController {
        		
        		protected ListGrid target;
        		
        
        		public WindowedDIDWizardController(HandlerManager eventBus, ListGrid target) {
        			super(eventBus);
        			this.target=target;
        		}
        		
        		public void bind()
        		{
        			/* Add HasChangedHandler for entryMethod */
        			HandlerRegistration entryMethodReg = entryMethod.addChangedHandler(new ChangedHandler(){
        				@Override
        				public void onChanged(ChangedEvent event) {
        					if(event.getValue() != null && event.getValue().equals(EntryMethod.RANGE_ENTRY.getName()))
        					{
        						setRangeEntryFormVisbility(Visibility.INHERIT);
        						setManualEntryFormVisibility(Visibility.HIDDEN);
        					}
        					else
        					{
        						setRangeEntryFormVisbility(Visibility.HIDDEN);
        						setManualEntryFormVisibility(Visibility.INHERIT);
        					}
        					
        				}
        			});
        			
        			/* Add binding for create button */
        			HandlerRegistration createButtonReg = createButton.addClickHandler(new ClickHandler(){
        
        				@Override
        				public void onClick(ClickEvent event) {
        					vm.saveData(new DIDWizardCallback());
        					
        				}
        				
        				
        			});
        			
        			this.addHandlerRegistration(entryMethodReg);
        		}
        		
        
        	}
        	
        	public class DIDWizardCallback implements DSCallback 
        	{
        
        		@Override
        		public void execute(DSResponse response, Object rawData,
        				DSRequest request) {
        			Log.debug("Call back executed.");
        			
        		}
        		
        	}
        
        }

        Code for the DAO object:

        Code:
        public class DIDWizardDAOImpl extends HibernateDaoSupport implements
        		DIDWizardDAO {
        
        	protected Logger LOGGER;
        	ObjectReferenceLoader objectReferenceLoader;
        	
        	public DIDWizardDAOImpl()
        	{
        		LOGGER = Logger.getLogger(DIDWizardDAOImpl.class);
        	}
        	
        	@Override
        	public DSResponse add(DSRequest dsRequest) throws Exception {
        		DIDWizardData wizardData= new DIDWizardData();
        		Map valueMap = dsRequest.getValues();
        		objectReferenceLoader.loadObjectReferences(wizardData, valueMap);
        		
        		LOGGER.info("add invoked for " + wizardData);
        		
        		DSResponse dsResponse = new DSResponse();
        		LOGGER.info("Performing Validation");
        		
        		ErrorReport errorReport = dsRequest.getDataSource().validate(DataTools.getProperties(wizardData), false);
        		if(errorReport!=null)
        		{
        			LOGGER.info("Validation errors detected.");
        			dsResponse.setStatus(DSResponse.STATUS_VALIDATION_ERROR);
        			dsResponse.setErrorReport(errorReport);
        			LOGGER.info(DataTools.prettyPrint(errorReport));
        			return dsResponse;
        		}
        		else
        		{
        			DIDWizardAddResult result = this.createDIDs(wizardData);
        			dsResponse.setData(result);
        			return dsResponse;
        		}
        	}
        	
        	protected DIDWizardAddResult createDIDs(DIDWizardData wizardData)
        	{
        	
        		List<DID> didList = new ArrayList<DID>();
        		List<String> addedList = new ArrayList<String>();
        		List<String> existList = new ArrayList<String>();
        		List<String> errorList = new ArrayList<String>();
        		
        		switch(wizardData.getEntryMethod())
        		{
        			case RANGE_ENTRY:
        				 didList = this.rangeEntry(wizardData,addedList, existList, errorList);
        			default:
        				 didList = this.manualEntry(wizardData,addedList, existList, errorList);	
        		}
        		
        		DIDWizardAddResult result = new DIDWizardAddResult();
        		result.setDidList(didList);
        		result.setAddList(addedList);
        		result.setExistList(existList);
        		result.setErrorList(errorList);
        		
        		return result;
        	}
        	
        	protected List<DID> rangeEntry(DIDWizardData wizardData, List<String> addedList, List<String> existList, List<String> errorList)
        	{
        		List<com.thinkingphones.voip.DID> didList = new ArrayList<com.thinkingphones.voip.DID>();
        		
        		for(int i=wizardData.getExtensionFrom(); i<=wizardData.getExtensionTo(); i++)
        		{
        			String curExtension = StringTools.lPad(""+i,4,"0");
        			com.thinkingphones.voip.DID curDID = null;
        			
        			String didStr= wizardData.getCountryDialCode()
        				+ "-" + wizardData.getNpa()
        				+ "-" + wizardData.getNxx()
        				+ "-" + curExtension;
        		
        			try
        			{
        				curDID = new com.thinkingphones.voip.DID(didStr, true);
        				didList.add(curDID);
        			}
        			catch(ParseException exc)
        			{
        				errorList.add(didStr + " will not be added, it's not a valid DID.");
        			}
        			
        		}
        		
        		return addDIDs(wizardData, addedList, existList, errorList, didList);
        		
        		
        	}
        	
        	protected List<DID> manualEntry(DIDWizardData wizardData,List<String> addedList, List<String> existList, List<String> errorList)
        	{
        
        			ArrayList<com.thinkingphones.voip.DID> didList = this.parseDIDs(wizardData.getManualEntry(), errorList);		
        			return addDIDs(wizardData, addedList, existList, errorList, didList);
        	}
        	
        	protected ArrayList<com.thinkingphones.voip.DID> parseDIDs(String didListValue, List<String> errorList)
        	{
        		LOGGER.debug("parsing " + didListValue);
        		
        		ArrayList<String> didStringList = new ArrayList<String>();
        		ArrayList<com.thinkingphones.voip.DID> didList = new ArrayList<com.thinkingphones.voip.DID>();
        		String[] didArrayComma = didListValue.split(",");
        		
        		for(String stringPart : didArrayComma)
        		{
        			if(stringPart.trim().length()  >0)
        			{
        				String[] subParts = stringPart.split("\n");
        				
        				for(String subPart : subParts)
        				{
        					if(subPart.trim().length() >0)
        					{
        						didStringList.add(subPart);
        					}
        				}
        				
        			}
        		}
        		
        		
        		
        		for(String didString : didStringList)
        		{
        			com.thinkingphones.voip.DID did;
        	
        			try
        			{
        				did  = new com.thinkingphones.voip.DID(didString);
        				didList.add(did);
        			}
        			catch(ParseException exc)
        			{
        				errorList.add(didString + " will not be added; it is not a valid DID.");
        			}
        		}
        		
        		LOGGER.debug("Parsed " + didList.size()+ " dids");
        		
        		return didList;
        	}
        	
        	protected List<DID> addDIDs(DIDWizardData wizardData, List<String> addedList, List<String> existList, List<String>errorList, List<com.thinkingphones.voip.DID> didList)
        	{
        		List<DID> fullDIDList = new ArrayList<DID>();
        	
        		for(com.thinkingphones.voip.DID currentDID: didList)
        		{
        		
        			Criteria didCriteria = this.getHibernateTemplate().getSessionFactory().getCurrentSession().createCriteria(DID.class);
        			
        			didCriteria.add(Restrictions.eq("fullDID", currentDID.getFormattedNumber(true, "")));
        		
        			List results  = didCriteria.list();
        			
        			if(results.size()> 0)
        			{
        				existList.add(currentDID.getFormattedNumber(true, ""));
        				fullDIDList.add((DID)results.get(0));
        			}
        			else
        			{
        				try
        				{
        					DID addedDID = this.createNewDID(wizardData, currentDID);
        					addedList.add(currentDID.getFormattedNumber(true, ""));
        					fullDIDList.add(addedDID);
        					
        				}
        				catch(DataAccessException exc)
        				{
        					LOGGER.error("Caught a DataAccessException while trying to persist DID", exc);
        					errorList.add(currentDID.getFormattedNumber(true, "") + " will not be added, a backend data exception occured.");
        				}
        			}
        		}
        		
        		return fullDIDList;
        	}
        
        		
        	protected DID createNewDID(DIDWizardData wizardData, com.thinkingphones.voip.DID did)
        	{
        		DID didToAdd = new DID();
        		didToAdd.setCountryDialCode(did.getCountryCode());
        		didToAdd.setNpa(did.getNpa());
        		didToAdd.setNxx(did.getNxx());
        		didToAdd.setStation(did.getExtension());
        		
        		if(wizardData.getAccountId()!= null)
        		{
        			didToAdd.setAccount(getAccount(wizardData.getAccountId()));
        		}
        		
        		if(wizardData.getCsaId()!= null)
        		{
        			didToAdd.setCsa(getCsa(wizardData.getCsaId()));
        		}
        		
        		if(wizardData.getCarrierId()!= null)
        		{
        			didToAdd.setCarrier(getCarrier(wizardData.getCarrierId()));
        		}
        		
        		setLocationInformation(didToAdd);	
        		
        		didToAdd.setPortStatus(DIDPortStatusEnum.RESERVED.getValue());
        		
        		didToAdd.setDateReserved(new Date().getTime());
        		
        		LOGGER.debug("Persisting " + didToAdd + " to Hibernate.");
        		this.getHibernateTemplate().save(didToAdd);
        		
        		return didToAdd;
        	}
        	
        	protected void setLocationInformation(DID didToAdd) 
        	{
        		LocationMapping locationMapping= null;
        		
        		locationMapping = getLocationBasedOnNxx(didToAdd);
        		
        		if(locationMapping == null)
        		{
        			locationMapping = getLocationBasedOnNPA(didToAdd);
        			if(locationMapping == null)
        			{
        				locationMapping = getLocationBasedOnCountry(didToAdd);
        			}
        		}
        		
        		if(locationMapping != null)
        		{
        			didToAdd.setCountry(locationMapping.getCountry());
        			didToAdd.setState(locationMapping.getState());
        			didToAdd.setLocation(locationMapping.getLocation());
        			didToAdd.setTollFree(locationMapping.getTollFree());
        			didToAdd.setLocalUse(locationMapping.getLocalUse());
        			didToAdd.setSacService(locationMapping.getSacService());
        			didToAdd.setInternalUse(locationMapping.getInternalUse());
        			
        		}
        	}
        	
        	protected LocationMapping getLocationBasedOnNxx(DID didToAdd)
        	{
        		LOGGER.debug("Attempting to resolve LocationMapping information for " + didToAdd + " based on nxx.");
        		Criteria criteria= this.getHibernateTemplate().getSessionFactory().getCurrentSession().createCriteria(LocationMapping.class);
        		
        		SimpleExpression nxxMatch = Restrictions.eq("nxx", didToAdd.getNxx());
        		SimpleExpression npaMatch= Restrictions.eq("npa", didToAdd.getNpa());
        		SimpleExpression countryMatch= Restrictions.eq("countryDialCode", didToAdd.getCountryDialCode());
        		Conjunction conjunction = Restrictions.conjunction();
        		conjunction.add(nxxMatch);
        		conjunction.add(npaMatch);
        		conjunction.add(countryMatch);
        		
        		criteria.add(conjunction);
        		
        		List results = criteria.list();
        		
        		if(results.size() != 1)
        		{
        			return null;
        		}
        		else
        		{
        			return (LocationMapping)results.get(0);
        		}
        	}
        	
        	
        	protected LocationMapping getLocationBasedOnNPA(DID didToAdd)
        	{
        		LOGGER.debug("Attempting to resolve LocationMapping information for " + didToAdd + " based on npa.");
        		Criteria criteria= this.getHibernateTemplate().getSessionFactory().getCurrentSession().createCriteria(LocationMapping.class);
        		
        		Criterion nxxNull = Restrictions.isNull("nxx");
        		SimpleExpression npaMatch= Restrictions.eq("npa", didToAdd.getNpa());
        		SimpleExpression countryMatch= Restrictions.eq("countryDialCode", didToAdd.getCountryDialCode());
        		Conjunction conjunction = Restrictions.conjunction();
        		conjunction.add(nxxNull);
        		conjunction.add(npaMatch);
        		conjunction.add(countryMatch);
        		
        		criteria.add(conjunction);
        		
        		List results = criteria.list();
        		
        		if(results.size() != 1)
        		{
        			return null;
        		}
        		else
        		{
        			return (LocationMapping)results.get(0);
        		}
        	}
        	
        	protected LocationMapping getLocationBasedOnCountry(DID didToAdd)
        	{
        		LOGGER.debug("Attempting to resolve LocationMapping information for " + didToAdd + " based on country dial code.");
        		Criteria criteria= this.getHibernateTemplate().getSessionFactory().getCurrentSession().createCriteria(LocationMapping.class);
        		
        		Criterion nxxNull = Restrictions.isNull("nxx");
        		Criterion npaNull= Restrictions.isNull("npa");
        		SimpleExpression countryMatch= Restrictions.eq("countryDialCode", didToAdd.getCountryDialCode());
        		Conjunction conjunction = Restrictions.conjunction();
        		conjunction.add(nxxNull);
        		conjunction.add(npaNull);
        		conjunction.add(countryMatch);
        		
        		criteria.add(conjunction);
        		
        		List results = criteria.list();
        		
        		if(results.size() != 1)
        		{
        			return null;
        		}
        		else
        		{
        			return (LocationMapping)results.get(0);
        		}
        	}
        
        	protected Account getAccount(Long id)
        	{
        		Criteria criteria= this.getHibernateTemplate().getSessionFactory().getCurrentSession().createCriteria(Account.class);
        		
        		criteria.add(Restrictions.eq("id", id));
        		
        		Object result = criteria.uniqueResult();
        		
        		if(result == null)
        		{
        			return null;
        		}
        		else
        		{
        			return (Account)result;
        		}
        	}
        	
        	protected Carrier getCarrier(Long id)
        	{
        		Criteria criteria= this.getHibernateTemplate().getSessionFactory().getCurrentSession().createCriteria(Carrier.class);
        		
        		criteria.add(Restrictions.eq("providerId", id));
        		
        		Object result = criteria.uniqueResult();
        		
        		if(result == null)
        		{
        			return null;
        		}
        		else
        		{
        			return (Carrier)result;
        		}
        	}
        	
        	protected CSA getCsa(Long id)
        	{
        		Criteria criteria= this.getHibernateTemplate().getSessionFactory().getCurrentSession().createCriteria(CSA.class);
        		
        		criteria.add(Restrictions.eq("id", id));
        		
        		Object result = criteria.uniqueResult();
        		
        		if(result == null)
        		{
        			return null;
        		}
        		else
        		{
        			return (CSA)result;
        		}
        	}
        
        	/**
        	 * @return the objectReferenceLoader
        	 */
        	public ObjectReferenceLoader getObjectReferenceLoader() {
        		return objectReferenceLoader;
        	}
        
        	/**
        	 * @param objectReferenceLoader the objectReferenceLoader to set
        	 */
        	public void setObjectReferenceLoader(ObjectReferenceLoader objectReferenceLoader) {
        		this.objectReferenceLoader = objectReferenceLoader;
        	}
        	
        	
        	
        }
        All of this is of course still a work in progress, the callback right now for instance just prints out a log message and the dataSource needs customValidators added to it; but that can come later - assuming I fill out all fields this should work right now.

        I'm not sure why the valuesManager is sending an update Operation instead of an addOperation. It's the first time I'm using a ValuesManager to be honest.
        Last edited by SiccoNaets; 9 Jul 2010, 14:00.

        Comment


          #5
          You can force the ValuesManager to use an "add" operation via setSaveOperationType().

          Comment


            #6
            Thanks Iso; that fixed it.

            I now have the form succesfully submitting data to the datasource and getting objects created. However, the one problem that remains is how to get some type of communication back to the client about what happened.

            I'm returning data to the user in an object like this:

            Code:
            public class DIDWizardAddResult {
            
            	List<DID> didList;
            	List<String> addList;
            	List<String> existList;
            	List<String> errorList;
            	/**
            	 * @return the didList
            	 */
            	public List<DID> getDidList() {
            		return didList;
            	}
            	/**
            	 * @param didList the didList to set
            	 */
            	public void setDidList(List<DID> didList) {
            		this.didList = didList;
            	}
            	/**
            	 * @return the addList
            	 */
            	public List<String> getAddList() {
            		return addList;
            	}
            	/**
            	 * @param addList the addList to set
            	 */
            	public void setAddList(List<String> addList) {
            		this.addList = addList;
            	}
            	/**
            	 * @return the existList
            	 */
            	public List<String> getExistList() {
            		return existList;
            	}
            	/**
            	 * @param existList the existList to set
            	 */
            	public void setExistList(List<String> existList) {
            		this.existList = existList;
            	}
            	/**
            	 * @return the errorList
            	 */
            	public List<String> getErrorList() {
            		return errorList;
            	}
            	/**
            	 * @param errorList the errorList to set
            	 */
            	public void setErrorList(List<String> errorList) {
            		this.errorList = errorList;
            	}
            	
            	
            }
            I'm basically populating the 4 lists in this wrapper object with the list of DIDs that were created, the ones that were succesfully added, the ones that already existed and the ones that simply failed. I then pass the DIDWizardAddResult back to the client by calling DSResponse.setData();

            However, in the callback on the client-side; all I see when I call getData() on the dsResponse object is that there is a single record with no attributes. The rawData that's provided back to the callback is of type javascriptObject, but also does not contain any data; i.e.:


            Code:
            public class DIDWizardCallback implements DSCallback 
            	{
            
            		@Override
            		public void execute(DSResponse response, Object rawData,
            				DSRequest request) {
            			Log.debug("Call back executed.");
            			
            			Record[] results = response.getData();
            			
            			Log.debug("Results contains " + results.length +" records.");
            			
            			for(Record result : results)
            			{
            				String[] attribs = result.getAttributes();
            				
            				for(String attrib : attribs)
            				{
            					Log.debug(attrib + ": " + result.getAttribute(attrib));
            				}
            			}
            			
            			Log.debug("Raw Data: " + rawData.toString());
            			
            			try
            			{
            				JavaScriptObject wizardResult = (JavaScriptObject)rawData;
            				Log.debug(wizardResult.toSource());
            			}
            			catch(Exception exc)
            			{
            				Log.error("Caught an error: "+ exc);
            			}
            		}
            		
            	}
            produces:

            Code:
            (-:-) 2010-07-12 15:56:22,559 [DEBUG] Call back executed.
            (-:-) 2010-07-12 15:56:22,566 [DEBUG] Results contains 1 records.
            (-:-) 2010-07-12 15:56:22,573 [DEBUG] Raw Data: [object Object]
            (-:-) 2010-07-12 15:56:22,577 [DEBUG] <null message>
            How does the the SmarGWT API map server objects back onto Records? I thought it used the ds.xml mapping file for that, but since in this case what I need is assymetric communication (i.e. submit data in one format and get data back in another format); this doesn't work.

            I'm wondering... should I instead modify my WizardData objec to have collections for the 4 lists I need to track; leave them empty when I submit the form; but then populate them on the server side so at least I'm getting the same object back as I'm submitting? Will that work?

            Comment


              #7
              If you want to just send back a blob of data that doesn't conform to any particular schema, set dropExtraFields=false on your DataSource definition.

              Comment


                #8
                Wow, awesome!

                That totally did the trick, thanks so much! I now get a JS representation of the WizardAddDataResult back in Record format :)

                One final question, if you don't mind - why was the ValueManager automatically put into UPDATE mode and did I have to switch it back manually? Is that because the .ds.xml file lack a primary key field?

                Comment


                  #9
                  Not really clear, but in terms of the DataSource protocol, without a primary key and without a conforming response, this isn't really either an "add" or an "update" so it's not clear that there is a correct behavior here.

                  Comment


                    #10
                    Gotcha. Thanks again!

                    Comment

                    Working...
                    X