Max field set size exceeded in Spartacus set-up - sap-commerce-cloud

I'm adding a new field to the ProductWSDTO and making the necessary additions to use this field on the spartacus frontend. But when I want to open PDP, I get the following error. How can I solve this problem?
de.hybris.platform.servicelayer.dto.converter.ConversionException: Max field set size exceeded. Reason of that can be : too generic configuration, lack of properly defined BASIC field set level for data class, reccurency in data structure
ERROR [hybrisHTTP8] [RestHandlerExceptionResolver] de.hybris.platform.servicelayer.dto.converter.ConversionException: Max field set size exceeded. Reason of that can be : too generic configuration, lack of properly defined BASIC field set level for data class, reccurency in data structure
at de.hybris.platform.webservicescommons.mapping.impl.DefaultFieldSetBuilder.addToFieldSet(DefaultFieldSetBuilder.java:536)
at de.hybris.platform.webservicescommons.mapping.impl.DefaultFieldSetBuilder.parseField(DefaultFieldSetBuilder.java:522)
at de.hybris.platform.webservicescommons.mapping.impl.DefaultFieldSetBuilder.parseComplexField(DefaultFieldSetBuilder.java:331)
at de.hybris.platform.webservicescommons.mapping.impl.DefaultFieldSetBuilder.createFieldSetInternal(DefaultFieldSetBuilder.java:217)
at de.hybris.platform.webservicescommons.mapping.impl.DefaultFieldSetBuilder.createFieldSetForLevel(DefaultFieldSetBuilder.java:286)
at de.hybris.platform.webservicescommons.mapping.impl.DefaultFieldSetBuilder.createFieldSetInternal(DefaultFieldSetBuilder.java:211)
at de.hybris.platform.webservicescommons.mapping.impl.DefaultFieldSetBuilder.parseField(DefaultFieldSetBuilder.java:517)
at de.hybris.platform.webservicescommons.mapping.impl.DefaultFieldSetBuilder.parseComplexField(DefaultFieldSetBuilder.java:331)
at de.hybris.platform.webservicescommons.mapping.impl.DefaultFieldSetBuilder.parseParametrizedTypeField(DefaultFieldSetBuilder.java:452)
at de.hybris.platform.webservicescommons.mapping.impl.DefaultFieldSetBuilder.parseComplexField(DefaultFieldSetBuilder.java:319)
at de.hybris.platform.webservicescommons.mapping.impl.DefaultFieldSetBuilder.createFieldSetInternal(DefaultFieldSetBuilder.java:217)
at de.hybris.platform.webservicescommons.mapping.impl.DefaultFieldSetBuilder.createFieldSetForLevel(DefaultFieldSetBuilder.java:286)
at de.hybris.platform.webservicescommons.mapping.impl.DefaultFieldSetBuilder.createFieldSetInternal(DefaultFieldSetBuilder.java:211)
at de.hybris.platform.webservicescommons.mapping.impl.DefaultFieldSetBuilder.parseField(DefaultFieldSetBuilder.java:517)
at de.hybris.platform.webservicescommons.mapping.impl.DefaultFieldSetBuilder.parseComplexField(DefaultFieldSetBuilder.java:331)
at de.hybris.platform.webservicescommons.mapping.impl.DefaultFieldSetBuilder.parseParametrizedTypeField(DefaultFieldSetBuilder.java:452)
at de.hybris.platform.webservicescommons.mapping.impl.DefaultFieldSetBuilder.parseComplexField(DefaultFieldSetBuilder.java:319)
at de.hybris.platform.webservicescommons.mapping.impl.DefaultFieldSetBuilder.createFieldSetInternal(DefaultFieldSetBuilder.java:217)
at de.hybris.platform.webservicescommons.mapping.impl.DefaultFieldSetBuilder.createFieldSetForLevel(DefaultFieldSetBuilder.java:286)
at de.hybris.platform.webservicescommons.mapping.impl.DefaultFieldSetBuilder.createFieldSetInternal(DefaultFieldSetBuilder.java:211)
at de.hybris.platform.webservicescommons.mapping.impl.DefaultFieldSetBuilder.parseField(DefaultFieldSetBuilder.java:517)
at de.hybris.platform.webservicescommons.mapping.impl.DefaultFieldSetBuilder.parseComplexField(DefaultFieldSetBuilder.java:331)
at de.hybris.platform.webservicescommons.mapping.impl.DefaultFieldSetBuilder.parseParametrizedTypeField(DefaultFieldSetBuilder.java:452)
at de.hybris.platform.webservicescommons.mapping.impl.DefaultFieldSetBuilder.parseComplexField(DefaultFieldSetBuilder.java:319)
at de.hybris.platform.webservicescommons.mapping.impl.DefaultFieldSetBuilder.createFieldSetInternal(DefaultFieldSetBuilder.java:217)
at de.hybris.platform.webservicescommons.mapping.impl.DefaultFieldSetBuilder.createFieldSetForLevel(DefaultFieldSetBuilder.java:286)
at de.hybris.platform.webservicescommons.mapping.impl.DefaultFieldSetBuilder.createFieldSetInternal(DefaultFieldSetBuilder.java:211)
at de.hybris.platform.webservicescommons.mapping.impl.DefaultFieldSetBuilder.parseField(DefaultFieldSetBuilder.java:517)
at de.hybris.platform.webservicescommons.mapping.impl.DefaultFieldSetBuilder.parseComplexField(DefaultFieldSetBuilder.java:331)
at de.hybris.platform.webservicescommons.mapping.impl.DefaultFieldSetBuilder.parseParametrizedTypeField(DefaultFieldSetBuilder.java:452)
at de.hybris.platform.webservicescommons.mapping.impl.DefaultFieldSetBuilder.parseComplexField(DefaultFieldSetBuilder.java:319)
at de.hybris.platform.webservicescommons.mapping.impl.DefaultFieldSetBuilder.createFieldSetInternal(DefaultFieldSetBuilder.java:217)
at de.hybris.platform.webservicescommons.mapping.impl.DefaultFieldSetBuilder.createFieldSetForLevel(DefaultFieldSetBuilder.java:286)
at de.hybris.platform.webservicescommons.mapping.impl.DefaultFieldSetBuilder.createFieldSetInternal(DefaultFieldSetBuilder.java:211)
at de.hybris.platform.webservicescommons.mapping.impl.DefaultFieldSetBuilder.parseField(DefaultFieldSetBuilder.java:517)
at de.hybris.platform.webservicescommons.mapping.impl.DefaultFieldSetBuilder.parseComplexField(DefaultFieldSetBuilder.java:331)
at de.hybris.platform.webservicescommons.mapping.impl.DefaultFieldSetBuilder.parseParametrizedTypeField(DefaultFieldSetBuilder.java:452)
at de.hybris.platform.webservicescommons.mapping.impl.DefaultFieldSetBuilder.parseComplexField(DefaultFieldSetBuilder.java:319)

#Rebia,
Below are the steps to avoid the above issue.
1.define a field mapper to fill up the new field in ProductWsDTO
added new field in fieldSetLevelMapping:
entry key="BASIC" value="totalPageCount,catalog,totalProductCount,currentPage,products,version,myProperty"
Inject right populator to populate value in WSDTO.
It seems your DTO is too complex so do changes in this file.
dto-level-mappings-v2-spring.xml
Here try to change defaultMaxFieldSetSize to 80k

This error is related to handling 'fields' parameter :
https://help.hybris.com/6.7.0/hcd/8c404c5886691014a48c88f4a49f9bf3.html
Section 'Fields configuration', 'Field Set Builder' : https://help.hybris.com/6.7.0/hcd/e571a0e6f7524d4ab62e29cc2b387aeb.html
Mapping mechanism in occ is based on Orika mapper.
There is additional filter which check if currently mapped attribute is on the list of attributes which should be mapped. That list is created based on class definition and fieldSetLevelMappings by fieldSetBuilder (defined in dto-level-mappings-v2-spring.xml)
<alias alias="fieldSetBuilder" name="defaultFieldSetBuilder"/>
<bean id="defaultFieldSetBuilder"
class="de.hybris.platform.webservicescommons.mapping.impl.DefaultFieldSetBuilder">
<property name="defaultRecurrencyLevel" value="4"/>
<property name="defaultMaxFieldSetSize" value="50000"/>
<property name="fieldSetLevelHelper" ref="fieldSetLevelHelper"/>
</bean>
There are two parameters which can influence how the field set builder works :
defaultRecurrencyLevel
defaultMaxFieldSetSize - If the limit is exceeded, an exception is thrown - this is exception you see.
It looks like for request you try to do - response dto is too complex
Verify if fieldsSetMapping for that dto class was defined properly.
If you don't specify any fields attribute in request - value is taken from DEFAULT fieldSetLevelMapping.
Try to specify fields attribute for your call and verify if you still get that exception.
Check this thread in SAP community for detail.
Best Regards,
Jerry

Related

Hazelcast distributed query using indexes

I am trying to query Hazelcast map using predicates. My predicate code works fine without indexes but for better performance, I want to put index on the key my Hazelcast map.
Map Structure: IMap<Event, Long> - Event is a POJO class.
<map name="event.map">
<in-memory-format>NATIVE</in-memory-format>
<backup-count>2</backup-count>
<async-backup-count>0</async-backup-count>
<time-to-live-seconds>30</time-to-live-seconds>
<max-idle-seconds>0</max-idle-seconds>
<eviction-policy>LFU</eviction-policy>
<max-size policy="FREE_NATIVE_MEMORY_PERCENTAGE">25</max-size>
<cache-deserialized-values>INDEX-ONLY</cache-deserialized-values>
<indexes>
<index ordered="true">eventType</index>
</indexes>
</map>
With the above Map config, I get following exception:
Jul 19, 2019 10:04:21 PM com.hazelcast.map.impl.operation.HDEntryOperation
SEVERE: [127.0.0.1]:5701 [dev] [3.11.2] java.lang.IllegalArgumentException: There is no suitable accessor for 'eventType' on class 'java.lang.Long'
com.hazelcast.query.QueryException: java.lang.IllegalArgumentException: There is no suitable accessor for 'eventType' on class 'java.lang.Long'
at com.hazelcast.query.impl.getters.ReflectionHelper.createGetter(ReflectionHelper.java:175)
at com.hazelcast.query.impl.getters.Extractors.instantiateGetter(Extractors.java:124)
at com.hazelcast.query.impl.getters.Extractors.getGetter(Extractors.java:101)
at com.hazelcast.query.impl.getters.Extractors.extract(Extractors.java:63)
at com.hazelcast.query.impl.QueryableEntry.extractAttributeValueFromTargetObject(QueryableEntry.java:144)
at com.hazelcast.query.impl.QueryableEntry.extractAttributeValue(QueryableEntry.java:82)
at com.hazelcast.query.impl.QueryableEntry.getAttributeValue(QueryableEntry.java:48)
at com.hazelcast.query.impl.QueryableEntry.getConverter(QueryableEntry.java:67)
at com.hazelcast.query.impl.IndexImpl.saveEntryIndex(IndexImpl.java:79)
at com.hazelcast.query.impl.Indexes.saveEntryIndex(Indexes.java:164)
at com.hazelcast.map.impl.recordstore.AbstractRecordStore.saveIndex(AbstractRecordStore.java:165)
at com.hazelcast.map.impl.recordstore.DefaultRecordStore.putInternal(DefaultRecordStore.java:709)
at com.hazelcast.map.impl.recordstore.DefaultRecordStore.setWithUncountedAccess(DefaultRecordStore.java:987)
at com.hazelcast.map.impl.operation.EntryOperator.onAddedOrUpdated(EntryOperator.java:288)
at com.hazelcast.map.impl.operation.EntryOperator.doPostOperateOps(EntryOperator.java:219)
at com.hazelcast.map.impl.operation.HDEntryOperation.runVanilla(HDEntryOperation.java:257)
at com.hazelcast.map.impl.operation.HDEntryOperation.runInternal(HDEntryOperation.java:95)
at com.hazelcast.map.impl.operation.HDMapOperation.run(HDMapOperation.java:88)
at com.hazelcast.spi.Operation.call(Operation.java:170)
at com.hazelcast.spi.impl.operationservice.impl.OperationRunnerImpl.call(OperationRunnerImpl.java:208)
at com.hazelcast.spi.impl.operationservice.impl.OperationRunnerImpl.run(OperationRunnerImpl.java:197)
at com.hazelcast.spi.impl.operationexecutor.impl.OperationExecutorImpl.run(OperationExecutorImpl.java:407)
at com.hazelcast.spi.impl.operationexecutor.impl.OperationExecutorImpl.runOrExecute(OperationExecutorImpl.java:434)
at com.hazelcast.spi.impl.operationservice.impl.Invocation.doInvokeLocal(Invocation.java:586)
at com.hazelcast.spi.impl.operationservice.impl.Invocation.doInvoke(Invocation.java:571)
at com.hazelcast.spi.impl.operationservice.impl.Invocation.invoke0(Invocation.java:530)
at com.hazelcast.spi.impl.operationservice.impl.Invocation.invoke(Invocation.java:220)
at com.hazelcast.spi.impl.operationservice.impl.InvocationBuilderImpl.invoke(InvocationBuilderImpl.java:60)
at com.hazelcast.client.impl.protocol.task.AbstractPartitionMessageTask.processMessage(AbstractPartitionMessageTask.java:67)
at com.hazelcast.client.impl.protocol.task.AbstractMessageTask.initializeAndProcessMessage(AbstractMessageTask.java:123)
at com.hazelcast.client.impl.protocol.task.AbstractMessageTask.doRun(AbstractMessageTask.java:111)
at com.hazelcast.client.impl.protocol.task.AbstractMessageTask.run(AbstractMessageTask.java:101)
at com.hazelcast.spi.impl.operationservice.impl.OperationRunnerImpl.run(OperationRunnerImpl.java:161)
at com.hazelcast.spi.impl.operationexecutor.impl.OperationThread.process(OperationThread.java:159)
at com.hazelcast.spi.impl.operationexecutor.impl.OperationThread.process(OperationThread.java:127)
at com.hazelcast.spi.impl.operationexecutor.impl.OperationThread.run(OperationThread.java:110)
Caused by: java.lang.IllegalArgumentException: There is no suitable accessor for 'eventType' on class 'java.lang.Long'
at com.hazelcast.query.impl.getters.ReflectionHelper.createGetter(ReflectionHelper.java:168)
... 35 more
From the exception I understand that Hazelcast is trying to apply the index on the Value field of the IMap.
Is there a way index can be put on the Key field of the IMap?
Try
<indexes>
<index ordered="true">__key.eventType</index>
</indexes>
As a key-value store, it's usual to search on the values, so that's what the index expects. When you put eventType in the index it looks for that field in
the value. You need to change it to __key.eventType to make it look in the key.
If you need frequent search access to part of a composite primary key, as the index implements, then the choice of primary key structure may need reviewed.
Also, if you can, upgrade from 3.11.2 to 3.12.1. There are some querying improvements behind the scenes.

Gateway method-name as header value

I have a Gateway with a default-request-channel an multiple methods.
public interface IUserService {
public void updateUserName(Long id, String username);
public void updatePassword(Long id, String password);
...
}
and the following xml config
...
<gateway id="userService" service-interface="...IUserService"
default-request-channel="dataRequestChannel"
default-reply-channel="dataResponseChannel" />
...
How can i get information about the method which is invoked ?
I know that it is possible to apply static header values but is that the only way ?
Or am i totally wrong ?
Thanks
We have an open JIRA issue for this feature; please vote it up.
Right now, the #method variable is available in expressions within specific method declarations
<int:gateway id="gw"
service-interface="foo.Gateway"
default-request-channel="input">
<int:method name="sendAndReceive">
<int:header name="gwMethod" expression="#method"/>
</int:method>
</int:gateway>
But you would still have to declare each method.
Perhaps another, relatively simple enhancement would be to support wildcards in method names; something like...
<int:gateway id="gw"
service-interface="foo.Gateway"
default-request-channel="input">
<int:method name="*">
<int:header name="gwMethod" expression="#method"/>
</int:method>
</int:gateway>
Where headers for method * would be added for all methods.
As of 3.0.1 you can do the following to set headers across all methods accessed via the gateway (you can also manage the specific methods as usual).
I am showing how to set two header properties, transportType and methodType, one dynamic and one static:
<int:gateway id="gw"
service-interface="foo.async.Gateway"
default-request-channel="gateway-request-channel"
default-reply-channel="gateway-response-channel">
<int:default-header name="transportType" value="async-msg"/>
<int:default-header name="methodType" expression="#gatewayMethod.name"/>
</int:gateway>
http://docs.spring.io/spring-integration/reference/html/messaging-endpoints-chapter.html#gateway-configuration-annotations
A bit late but the correct solution for those following this thread later.

How to register custom ISiteMapNodeUrlResolver in MvcSiteMapProviderRegistry

For the MvcSiteMapProvider v4, I've written a custom sitemap url resolver by overriding SiteMapNodeUrlResolverBase.
But I don't know how to register it in the MvcSiteMapProviderRegistry to make sure that a node's Url is always resolved using my own SiteMapNodeUrlResolver.
I expected something like:
this.For<ISiteMapNodeUrlResolver>().Use<MyUrlResolver>();
But this doesn't work, how should I do this?
The SiteMapNodeUrlResolvers (along with visibility providers and dynamic node providers) use a strategy pattern so you can wire up multiple instances and then refer to each instance by name. That name is then used by the AppliesTo() method to determine which URL resolver to use for each node.
If you inherit from SiteMapNodeUrlResolverBase rather than implement ISiteMapNodeUrlResolver, the default implementation of AppliesTo() will work in most cases. Then, this line (which is already included in the module by default) will automatically wire up all SiteMapNodeUrlResolvers:
// Multiple implementations of strategy based extension points
CommonConventions.RegisterAllImplementationsOfInterface(
(interfaceType, implementationType) => this.For(interfaceType).Singleton().Use(implementationType),
multipleImplementationTypes,
allAssemblies,
excludeTypes,
"^Composite");
By default, it only scans MvcSiteMapProvider.dll and your MVC project. If you have your URL resolver defined in a separate assembly, you will need to modify the allAssemblies variable to ensure that it includes your custom assembly.
Once it is loaded, then you need to call it by name. The default implementation uses the "ShortAssemblyQualifiedName", which is the same string that you would normally use to refer to a type in a configuration file (as long as your assembly is not strong named).
<mvcSiteMapNode title="Home" action="Index" controller="Home" urlResolver="MyNamespace.MySiteMapNodeUrlResolver, MyAssembly" />
The urlResolver property/attribute must be set on every node you wish to override the default implementation on.
If you prefer, you can implement the AppliesTo() method yourself so you can shorten the amount of configuration that is required. Note this will only work when using an external DI container because the internal DI container uses the type names from the configuration to instantiate the objects.
public override bool AppliesTo(string providerName)
{
return "myUrlResolver".Equals(providerName, StringComparison.InvariantCulture);
}
<mvcSiteMapNode title="Home" action="Index" controller="Home" urlResolver="myUrlResolver" />

WSImport generates conflicting XMLTypes for multiple Dynamics CRM 4.0 WSDL's

I'm currently working with the Dynamics CRM 4.0 webservice. First thing I did, was generating the right classes with wsimport for Java/JAX-WS based on the WSDL of the webservice. While generating the classes I got some errors:
[ERROR] A class/interface with the same name
"com.microsoft.schemas.crm._2007.webservices.RetrieveResponse" is already in use. Use a class customization to resolve this conflict.
line 979 of file://src/main/webapp/WEB-INF/classes/META-INF/wsdl/CrmServiceWsdl.wsdl
[ERROR] (Relevant to above error) another "RetrieveResponse" is generated from here.
line 12274 of file://src/main/webapp/WEB-INF/classes/META-INF/wsdl/CrmServiceWsdl.wsdl
Line 979 tells us:
<s:element name="RetrieveResponse">
<s:complexType>
<s:sequence>
<s:element name="RetrieveResult" type="s3:BusinessEntity" />
</s:sequence>
</s:complexType>
</s:element>
And line 12274 gives us:
<s:complexType name="RetrieveResponse">
<s:complexContent mixed="false">
<s:extension base="tns:Response">
<s:sequence>
<s:element ref="s3:BusinessEntity" />
</s:sequence>
</s:extension>
</s:complexContent>
</s:complexType>
Both parts are in the same namespace. Both will be generated as RetrieveResponse.class and so they are colliding. I've found a solution for this problem which is the JAX-B binding xml file:
<bindings node="//xsd:complexType[#name='RetrieveResponse']">
<jaxb:class name="RetrieveResponseType"/>
</bindings>
This works (not sure if this is the correct approach..?)..
So after this, I've managed to create some successful calls to the webservice, which is great!
Now comes the problem: some business entities in dynamics crm uses the class Picklist. This type of entity can be queried with the Metadata service: http://msdn.microsoft.com/en-us/library/bb890248.aspx
So the next thing I did was, again, generating the classes for the metadata service, based on it's WSDL. The result of the generated classes are not as we except. For example, it generates a class 'com.microsoft.schemas.crm._2007.webservices.ExecuteResponse'. But this class also exists in the exact same package of the CrmService generated classes. Differences between the 2 are:
Metadataservice ExecuteReponse:
#XmlAccessorType(XmlAccessType.FIELD)
#XmlType(name = "", propOrder = {
"response"
})
#XmlRootElement(name = "ExecuteResponse")
public class ExecuteResponse {
#XmlElement(name = "Response")
protected MetadataServiceResponse response;
etc...
CrmService ExecuteReponse:
#XmlAccessorType(XmlAccessType.FIELD)
#XmlType(name = "", propOrder = {
"response"
})
#XmlRootElement(name = "ExecuteResponse")
public class ExecuteResponse {
#XmlElement(name = "Response", required = true)
protected ResponseType response;
etc...
Now this class is just one example (another example is CrmAuthenticationToken), which is a almost exact duplicate of another class. To be able to use the same classes, I've added a package-suffix to the CrmService classes (displayed as prefix.).
So now when I try to call the CrmService, I get the following exception:
Two classes have the same XML type name "{http://schemas.microsoft.com/crm/2007/CoreTypes}CrmAuthenticationToken". Use #XmlType.name and #XmlType.namespace to assign different names to them.
this problem is related to the following location:
at com.microsoft.schemas.crm._2007.coretypes.CrmAuthenticationToken
at public com.microsoft.schemas.crm._2007.coretypes.CrmAuthenticationToken *prefix*.com.microsoft.schemas.crm._2007.coretypes.ObjectFactory.createCrmAuthenticationToken()
at *prefix*.com.microsoft.schemas.crm._2007.coretypes.ObjectFactory
this problem is related to the following location:
at *prefix*.com.microsoft.schemas.crm._2007.coretypes.CrmAuthenticationToken
at public javax.xml.bind.JAXBElement *prefix*.com.microsoft.schemas.crm._2007.webservices.ObjectFactory.createCrmAuthenticationToken(*prefix*.com.microsoft.schemas.crm._2007.coretypes.CrmAuthenticationToken)
at *prefix*.com.microsoft.schemas.crm._2007.webservices.ObjectFactory
I personally think it's weird they put different classes with the same name in the same package structure. This means you can never use the 2 webservices at the same time..
Is this a Microsoft, a WSimport bug or just a stupid mistake at my end? Hope somebody can help me with this problem!
Thanks for your time!
This is Microsoft inconsistency combined with wsimport being somewhat hard to use.
The PickList and the CRMAuthenticationToken sound like custom datatypes, you'd expect for these to get reused from service to service.
You'd also expect certain CRM-specific entities (say, Customer or Business or Address) to get reused from service to service.
It is bad manners on the Microsoft side of things that they define these differently for different services. This makes it hard to take the answer of one service and send it on to another service.
Had the services shared one or more common schemas, you could've compiled those first, using xjc. Then you could've provided a so-called episode file to wsimport to tell it to use those classes instead of generating new ones. See the metro guide. This is quite a puzzle, I can tell you from experience, I ran into bug JAXB-829, xjc forgets to generate if-exists attributes in the episode file.
What I'd do, I'd compile each wsdl to its own package and treat the generated classes as simple unintelligent Data Transfer Objects.
If I wanted to send an object I'd just retrieved from one service on to a second service, I'd convert between the both.
If this results in terribly unwieldy code, or if you wish to add logic to certain entities, I'd suggest you write your own proper model classes for the Entities you wish to share and write converters to and from the DTO objects in the web services packages you wish to use them with.

How to restrict amount of special content_type objects in folderish type to only one object?

I am creating a folderish type (archetype 1) and I want to have possibility to add only a single object of (archetype 2) to this folder.
You can restrict the addable types inside your folderish type ("archetype 1") to "archetype 2" by amending the "archetypes 1" types definition (profiles/default/archetype1.xml):
<?xml version="1.0"?>
<object name="archetype1">
...
<property name="filter_content_types">True</property>
<property name="allowed_content_types">
<element value="archetype2" />
</property>
...
</object>
Ok, so you want your second type archetype 2 to be addable only once inside archetype 1?
I would do it in such a way that the Add New dropdown on the green edit bar only shows archetype 2 if it can be added (the other solutions here require the user to first render the add form and then be told that this is not allowed).
You need to make sure that your folderish archetype 1 subclasses ConstrainTypesMixin.
I think if you use the folderish content types in Products.ATContentTypes you will automatically subclass this mixin class, but it helps to make sure.
Then, inside archetype 1, add the method: getLocallyAllowedTypes. This method is declared in the ConstrainTypesMixin class in Products/ATContentTypes/lib/constraintypes.py
In this method you can now add the logic to check if an instance of archetype 2 has already been added. If it has, don't return it as one of the locally allowed types. If it hasn't, then return it (with the other types if they exist).
Make sure to first call super() in this method to get the locally added types from the superclass's method.
To understand how this works, you can look at the *_addableTypesInContext* method in the FactoriesSubMenuItem class in plone/app/contentmenu/menu.py to see when and how this getLocallyAllowedTypes method is called.
You are probably best off creating a custom add form (perhaps using z3c.form) and making the placing the restriction there.
You can override the createObject.cpy script and add a check there:
this_type = REQUEST.form.get('type_name')
if this_type == 'MyATContentType':
# MyATContentType needs a special check
ctool = getToolByName(context, 'portal_catalog')
this_path = '/'.join(context.getPhysicalPath())
# Query the Catalog to see we already have an instance of this object here
results = ctool.searchResults({'portal_type': this_type, 'path': this_path})
if results:
context.plone_utils.addPortalMessage(_(
u'Sorry, but there already is an object of type %s here.' % this_type
))
# Redirect to the edit form of the first found object.
cv = results[0]
cv_path = cv.getPath()
return context.REQUEST.RESPONSE.redirect(cv_path + "/edit")
Provide the customized script, together with the associated .metadata file, in the skins/templates folder of your product.
Bonus tip: In Dexterity, you would add this check in dexterity.AddForm.update()

Resources