Showing posts with label Oracle Fusion Middleware 11g. Show all posts
Showing posts with label Oracle Fusion Middleware 11g. Show all posts

Saturday, June 23, 2012

OIM 11.1.1.5 Develop Connectors using ICF and SPI APIS.. Part 2

OIM 11.1.1.5 Develop Connectors using ICF and SPI APIS.. Part 2 Create connector class for the Flat File Connector by implementing the org.identityconnectors.framework.spi.Connector interface.

This class implements the CreateOp, DeleteOp, SearchOp and UpdateOp interfaces and thus supports all four operations. The FlatFileMetadata, FlatFileParser and FlatFileWriter classes are supporting classes.

package org.identityconnectors.flatfile;

import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;

import org.identityconnectors.flatfile.io.FlatFileIOFactory;
import org.identityconnectors.flatfile.io.FlatFileMetadata;
import org.identityconnectors.flatfile.io.FlatFileParser;
import org.identityconnectors.flatfile.io.FlatFileWriter;
import org.identityconnectors.framework.api.operations.GetApiOp;
import org.identityconnectors.framework.common.exceptions.AlreadyExistsException;
import org.identityconnectors.framework.common.exceptions.ConnectorException;
import org.identityconnectors.framework.common.objects.Attribute;
import org.identityconnectors.framework.common.objects.AttributeInfo;
import org.identityconnectors.framework.common.objects.AttributeInfoBuilder;
import org.identityconnectors.framework.common.objects.ConnectorObject;
import org.identityconnectors.framework.common.objects.ConnectorObjectBuilder;
import org.identityconnectors.framework.common.objects.ObjectClass;
import org.identityconnectors.framework.common.objects.OperationOptions;
import org.identityconnectors.framework.common.objects.ResultsHandler;
import org.identityconnectors.framework.common.objects.Schema;
import org.identityconnectors.framework.common.objects.SchemaBuilder;
import org.identityconnectors.framework.common.objects.Uid;
import org.identityconnectors.framework.common.objects.filter.AbstractFilterTranslator;
import org.identityconnectors.framework.common.objects.filter.FilterTranslator;
import org.identityconnectors.framework.spi.Configuration;
import org.identityconnectors.framework.spi.ConnectorClass;
import org.identityconnectors.framework.spi.PoolableConnector;
import org.identityconnectors.framework.spi.operations.CreateOp;
import org.identityconnectors.framework.spi.operations.DeleteOp;
import org.identityconnectors.framework.spi.operations.SchemaOp;
import org.identityconnectors.framework.spi.operations.SearchOp;
import org.identityconnectors.framework.spi.operations.UpdateOp;

/**
 * The main connector class
 */
@ConnectorClass(configurationClass = FlatFileConfiguration.class, displayNameKey = "FlatFile")
public class FlatFileConnector implements SchemaOp, CreateOp, DeleteOp,
        UpdateOp, SearchOp<Map<String, String>>, GetApiOp, PoolableConnector {

    private FlatFileConfiguration flatFileConfig;
    private FlatFileMetadata flatFileMetadata;
    private FlatFileParser flatFileParser;
    private FlatFileWriter flatFileWriter;
    private boolean alive = false;

    @Override
    public Configuration getConfiguration() {
        return this.flatFileConfig;
    }

    @Override
    public void init(Configuration config) {
        this.flatFileConfig = (FlatFileConfiguration) config;

        FlatFileIOFactory flatFileIOFactory =
             FlatFileIOFactory.getInstance(flatFileConfig);
        this.flatFileMetadata = flatFileIOFactory.getMetadataInstance();
        this.flatFileParser = flatFileIOFactory.getFileParserInstance();
        this.flatFileWriter = flatFileIOFactory.getFileWriterInstance();
        this.alive = true;
        System.out.println("init called: Initialization done");
    }

    @Override
    public void dispose() {
        this.alive = false;
    }

    @Override
    public Schema schema() {
        SchemaBuilder flatFileSchemaBldr = new SchemaBuilder(this.getClass());
        Set<AttributeInfo> attrInfos = new HashSet<AttributeInfo>();
        for (String fieldName : flatFileMetadata.getOrderedTextFieldNames()) {
            AttributeInfoBuilder attrBuilder = new AttributeInfoBuilder();
            attrBuilder.setName(fieldName);
            attrBuilder.setCreateable(true);
            attrBuilder.setUpdateable(true);
            attrInfos.add(attrBuilder.build());
        }
       
        // Supported class and attributes
        flatFileSchemaBldr.defineObjectClass
          (ObjectClass.ACCOUNT.getDisplayNameKey(),attrInfos);
        System.out.println("schema called: Built the schema properly");
        return flatFileSchemaBldr.build();
    }

    @Override
    public Uid create(ObjectClass arg0, Set<Attribute> attrs,
            OperationOptions ops) {

        System.out.println("Creating user account " + attrs);
        assertUserObjectClass(arg0);
        try {
            FlatFileUserAccount accountRecord = new FlatFileUserAccount(attrs);
            // Assert uid is there
            assertUidPresence(accountRecord);

            // Create the user
            this.flatFileWriter.addAccount(accountRecord);

            // Return uid
            String uniqueAttrField = this.flatFileConfig
                    .getUniqueAttributeName();
            String uniqueAttrVal = accountRecord
                    .getAttributeValue(uniqueAttrField);
            System.out.println("User " + uniqueAttrVal + " created");
           
            return new Uid(uniqueAttrVal);
        } catch (Exception ex) {

            // If account exists
            if (ex.getMessage().contains("exists"))
                throw new AlreadyExistsException(ex);

            // For all other causes
            System.out.println("Error in create " + ex.getMessage());
            throw ConnectorException.wrap(ex);
        }
    }

    @Override
    public void delete(ObjectClass arg0, Uid arg1, OperationOptions arg2) {
        final String uidVal = arg1.getUidValue();
        this.flatFileWriter.deleteAccount(uidVal);
        System.out.println("Account " + uidVal + " deleted");
    }

    @Override
    public Uid update(ObjectClass arg0, Uid arg1, Set<Attribute> arg2,
            OperationOptions arg3) {
        String accountIdentifier = arg1.getUidValue();
        // Fetch the account
        FlatFileUserAccount accountToBeUpdated = this.flatFileParser
                .getAccount(accountIdentifier);

        // Update
        accountToBeUpdated.updateAttributes(arg2);
        this.flatFileWriter
                .modifyAccount(accountIdentifier, accountToBeUpdated);
        System.out.println("Account " + accountIdentifier + " updated");

        // Return new uid
        String newAccountIdentifier = accountToBeUpdated
                .getAttributeValue(this.flatFileConfig.getUniqueAttributeName());
        return new Uid(newAccountIdentifier);
    }

    @Override
    public FilterTranslator<Map<String, String>> createFilterTranslator(
            ObjectClass arg0, OperationOptions arg1) {
        // TODO: Create a fine grained filter translator

        // Return a dummy object as its not applicable here.
        // All processing happens in the execute query
        return new AbstractFilterTranslator<Map<String, String>>() {
        };
    }

    @Override
    public ConnectorObject getObject(ObjectClass arg0, Uid uid,
            OperationOptions arg2) {
        // Return matching record
        String accountIdentifier = uid.getUidValue();
        FlatFileUserAccount userAcc = this.flatFileParser
                .getAccount(accountIdentifier);
        ConnectorObject userAccConnObject = convertToConnectorObject(userAcc);
        return userAccConnObject;
    }

    /*
     * (non-Javadoc)
     * This is the search implementation.
     * The Map passed as the query here, will map to all the records with
     * matching attributes.
     *
     * The record will be filtered if any of the matching attributes are not
     * found
     *
     * @see
     * org.identityconnectors.framework.spi.operations.SearchOp#executeQuery
     * (org.identityconnectors.framework.common.objects.ObjectClass,
     * java.lang.Object,
     * org.identityconnectors.framework.common.objects.ResultsHandler,
     * org.identityconnectors.framework.common.objects.OperationOptions)
     */
    @Override
    public void executeQuery(ObjectClass objectClass,
            Map<String, String> matchSet, ResultsHandler resultHandler,
            OperationOptions ops) {

    System.out.println("Inside executeQuery");
   
        // Iterate over the records and handle individually
        Iterator<FlatFileUserAccount> userAccountIterator = this.flatFileParser
                .getAccountIterator(matchSet);

        while (userAccountIterator.hasNext()) {
            FlatFileUserAccount userAcc = userAccountIterator.next();
            ConnectorObject userAccObject = convertToConnectorObject(userAcc);
            if (!resultHandler.handle(userAccObject)) {
                System.out.println("Not able to handle " + userAcc);
                break;
            }
        }
    }

    private void assertUserObjectClass(ObjectClass arg0) {
        if (!arg0.equals(ObjectClass.ACCOUNT))
            throw new UnsupportedOperationException(
                    "Only user account operations supported.");

    }

    private void assertUidPresence(FlatFileUserAccount accountRecord) {
        String uniqueAttrField = this.flatFileConfig.getUniqueAttributeName();
        String uniqueAttrVal = accountRecord.getAttributeValue(uniqueAttrField);

        if (uniqueAttrVal == null) {
            throw new IllegalArgumentException("Unique attribute not passed");
        }
    }

    private ConnectorObject convertToConnectorObject(FlatFileUserAccount userAcc) {
        ConnectorObjectBuilder userObjBuilder = new ConnectorObjectBuilder();
        // Add attributes
        List<String> attributeNames = this.flatFileMetadata
                .getOrderedTextFieldNames();
        for (String attributeName : attributeNames) {
            String attributeVal = userAcc.getAttributeValue(attributeName);
            userObjBuilder.addAttribute(attributeName, attributeVal);

            if (attributeName.equals(this.flatFileConfig
                    .getUniqueAttributeName())) {
                userObjBuilder.setUid(attributeVal);
                userObjBuilder.setName(attributeVal);
            }
        }
        return userObjBuilder.build();
    }

    @Override
    public void checkAlive() {
        if (!alive)
            throw new RuntimeException("Connection not alive");
    }

}

########

Friday, May 11, 2012

Oracle Fusion Middleware 11g (ofm11g) on Centos 5

Because of a little illness Lucas was a bit faster with the publish of his article :) but i decided to still post it on my blog.
In the new Fusion 11g, Oracle added a new extension to the bpel language for the coordination of master- and detail processes.
With this add on it is possible to relate a master process with his detail processes and give signals to each other when to start and when parts are finished.

Let’s start with building something
We created a new composite application and added 3 bpel processes to it.
The first process is the master process and this one will invoke 2 detail processes.
The idea is to let both detail process only get started when they receive a ‘signal’ from the master processes. If they receive it,
they’ll execute their own logic and when either finished or at some point they want to communicate back to the master process, they give a ‘signal’ back.
In the master process, after the the receive add the first signal

1<bpelx:signal name="doDetail1" label="doDetail1" to="details"/>
This will give the first detail process a signal so it will be able to receive
Now we need to invoke the first detail process
1<invoke name="invokeDetail"
2        inputVariable="invokeDetail_process_InputVariable"
3        partnerLink="MyDetail.mydetail_client" portType="ns1:MyDetail"
4        operation="process"
5        bpelx:detailLabel="detailProcessComplete1"
6        bpelx:invokeAsDetail="true">
Two things in here are needed. The first is the bpelx:invokeAsDetail=”true” and the second is the bpelx:detailLabel=”detailProcessComplete1″
The last property is used in cases in which we have multiple detail processes and the signals need to get correlated back with the master process.
Now lets see what is needed in the first detail process to receive the signal and eventually send one back to the master process.
The first activity after the receive is the receive signal
1<bpelx:receiveSignal name="WaitOnSignalFromMaster"
2                     label="doDetail1" from="master"/>
This will receive the signal from its master process and after that will carry on. Without the signal the detail process won’t get started. So you will only be able to trigger this detail/sub process from it’s master process.
After receiving the signal the detail process can execute some logic and eventually send back the signal to the master process
1<bpelx:signal name="ReplyBackToMaster"
2              label="detailProcessComplete1" to="master"/>
In the master process we use the receiveSignal activity to receive it back from the detail process and go on with the process.
1<bpelx:receiveSignal name="waitForDetailProcess1"
2                     label="detailProcessComplete1" from="details"/>
The same code is needed to invoke the second detail process. It’s up to you to decide wether or not the second detail process can run parallel to the first one.
If this is not the case we need to wait on the receiveSignal from the first detail process before we can start the second one.

If both processes can execute their logic and eventually send the signal back we can also receive both signals on the end of the master process. If both signals are receive we can do the callback because the whole process (master and all his details have been completed).
And in this case we can invoke the detail processes like oneway processes, but still inform the master process about the state of all these oneway (long running?) processes. At the end aggregate all the signals and do the callback or some other activity which is needed as soon all the detail processes reported back.

Testcase1
What will happen if we remove one of the receiveSignals from the Master process.

the master process will wait on the receivesignal activity until it receveid it.
Testcase2
What will happen when we do not send the signal from the master process to the detail process but leave the receiveSignal in the detail process.


The master process will wait on the receiveSignal from detail process1. Since the invoke of the both detail processes are oneway and we do the ‘check’ on the receiveSignals at the end, detail process1 will just complete and send the signal back to the master process. After this the master process will keep on waiting on the signal from detail process1.
Testcase3, when all just works




Eventually the flow of the master process can look like this.
In here i placed the receiveSignals to the end of the process. You can also place them after the invokes of the detail processes, so you’re sure they will wait on each other.

########