Advertisements

Archive for the 'BPEL' Category

Using Translate activity for XML to JSON

In this post, we will discuss about using Translate activity for converting XML to JSON. This is one of the functionalities I wanted to try since release of 12c as nxsd has been enhanced to handle JSON as well. So I tried this using BPEL in both 12.1.3 and 12.2.1 but found to be not working as expected. Hence I will just mention about approach to use Translate activity. And will show you how its working with File Adapter.

Create a BPEL process and drag Translate activity.

translate

trbpel

Click gear icon to define NXSD schema as per required JSON data.

native

Finish the wizard as shown below to create NXSD.

step1

step2

step3

step4

finish

Create BPEL variable varJson of NXSD complex type as shown below.

varjson

Create XSLT transformation to transform the input variable to varJson. For simplicity, we considered the NXSD structure almost similar to inputVariable.

xform

xform1

Now modify Translate activity by giving required values for all other fields.

translate2

Click OK. Now our BPEL process should look like below.

bpel

Deploy the composite and test to observe the following error.

input

error

This seems to be a bug as I observed same error in which ever way I tried. So if any readers tried this and able to get it right please do let me know.

However, the same XML to JSON translation is working as expected with File Adapter. Create file adapter using below screenshots using Write operation.

filestep1

filestep2

filestep3

filestep4

filestep5

filestep6

Now in BPEL process, create invoke activity to invoke the file adapter.

invokefilewrite

composite

Now create XSLT to transform inputVariable to inputFileWrite.

xformtofile

xform

Now deploy composite again and test using same input as above. Now  we will observe an output file in directory D as shown below.

ddir

json

You can find a sample project here having both of the cases discussed above.

Advertisements

Links

I will use this post as a repository of useful links and blogs which I can expect to grow exponentially over the time, so will try to include links on more generalized or important topics of much interest.

– SOA Suite PS6 New Features

Generic:

– SOA SDLC by Mark Nelson

ADF

– Understanding View Accessors

Adapters

-12c JMS Adapter Scalability

Weblogic

Cluster Messaging Protocols

Adapters

– Multiple SOA Suite Revisions with DB Adapter

Mediator

– Message Sequencing Patterns (white paper)

– Parallel Routing Rules

BPEL

BPEL Mania

List of All Instance States

– Transaction Boundaries and Rollbacks

Fault Handling

OTN Article Series

Fault Handling in SOA Suite – Advanced Concepts

Business Rules

Self-Study Course

EDN

EDN Debugging

– Fanning out Events

– SOA 12c – EDN Articles

Continuous Integration

Deploy SOA Composites using Hudson

OSB

Best Practices

– 11g Hands-on Tutorial

– WLST Fundamentals in context of OSB

– Working with Oracle Security Token Service

– One-Way Authentication Policies in OSB

– Coherence Integration

– Caching Strategies for OSB 11g

Out-of-Process Coherence Servers

– 12c Hands-on Tutorial

12c – Series of Articles

– 12c – REST enabling SOA

OWSM

Oracle Blog

BPM

    BPM vs BPEL

     Case Management

Quick Series

Case Activities, Part-I

SOA Governance

 – SOA Governance Through Enterprise Architecture

Fusion Applications

 Fusion Applications Life Cycle

 – Request Flow in Fusion Applications

 – Asynchronous Web Services in Fusion Applications

 – Oracle Fusion DOO – Integrating External Systems using EIL

Customizations

   – Translating Customizations

Transactions when using DB Adapter vs ADF BC Service

Most of us are well aware of global transactions (XA) when using DB Adapter and setting XADataSourceName during creation of DB Connection Pool. ADF BC Services are widely used in Oracle Fusion Applications at Data Service layer  instead DB Adapters. So it’s worth investigating or finding out the scope of transaction when calling these ADF BC Services.

Sample code for this demonstration can be found here and accepts string input, where valid values are ‘DB’ or ‘ADF’.

Conclusion:

  • ADF BC Service call does not get enlisted into main BPEL txn, so system fault, user defined fault or Roll back fault will not revert the changes done by ADF BC Service. So developer should think of compensation in failure scenarios in case of faults.

Business Events in Action

The purpose of this blog post is to explore subscription consistency levels. Subscriber can use any of the following consistency levels while subscribing to business events raised by publisher:

    • immediate
    • guaranteed
    • one and only one

Following is an extract from documentation explaining the functionality of each consistency level:

    • immediate – Events are delivered to the subscriber in the same global transaction and same thread as the publisher. The publish call does not return until all immediate subscribers have completed processing. If any subscribers throw an exception, no additional subscribers are invoked and an exception is thrown to publisher. The transaction is rolled back in case of any error during immediate processing
    • guaranteed – Events are delivered to the subscriber asynchronously without a global transaction. The subscriber can choose to create its own local transaction for processing, but it is committed independently of the rest of event processing. In addition, EDN does not attempt to resend an event (regardless of the backing store being AQ or JMS). If one or more subscribers fail to consume the event (while others succeed), those subscribers lose the message.
    • one and only one – Events are delivered to the subscriber in its own global (that is, JTA) transaction. Any changes made by the subscriber within that transaction are committed after the event processing is complete. If the subscriber fails, the transaction is rolled back. Failed events are retried a configured number of times.

This document shows you business events in action from the perspective of above highlighted points and might help you in choosing the right consistency level depending on the scenario.

Assumptions:

    • Using EDN-DB, though observations noted here will not vary when used EDN-JMS.
    • No clustered environment.

Use case:

A mediator raises 2 business events CreateOrder and UpdateOrder. For CreateOrder , we have the following subscribers:

    • Mediator routing the request to ASync BPEL (within same composite)
    • Mediator routing the request to Sync BPEL (within same composite)
    • Asynchronous BPEL process (within same composite)
    • Mediator throws exception while routing the request to Asynchronous BPEL (in different composite)

For UpdateOrder, we have the following subscribers:

    • Mediator routing the request to Asynchronous BPEL (in different composite)

We will observe the behavior in different scenarios by modifying event subscription consistency levels for above subscribers. The exception during the mediator routing is simulated by adding <onReply/> tag which is not needed in actual when routing to Async BPEL process. Example is shown below:

<switch>

<case executionType="direct"

name="Order2BPELProcess1.order2bpelprocess1_client.process">

<action>

<transform>

<part name="$out.payload"

function="xslt(xsl/CreateOrder_To_process.xsl, $in.payload)"/>

</transform>

<invoke reference="Order2BPELProcess1.order2bpelprocess1_client"

operation="process">

<onReply/>

</invoke>

</action>

</case>

</switch>

Configuration:

The retry configuration for one and only one subscribed events and the other EDN configuration can be set in EM console by navigating to soa-infra -> Administration -> System MBean Browser -> Application Defined MBeans -> oracle.as.soainfra.config -> EDN Config -> edn. This will bring up the following screen showing the default parameters.

event retry

Demonstration:

To start with, have a look at the list of events being used and it’s subscriptions below:

events

subsc

Consistency Level – immediate:

Modify all the event subscriptions to immediate consistency level.

immediate

Now raise the CreateOrder event and observe flow trace shown below. Since consistency level is immediate and the exception is thrown by one of the subscribers not all of the subscribers have received the event at all. And also no retry happened in case of faulted subscriber.

flowtrace1

Also we can’t find this event in event recovery console which is shown below:

immediaterecover

To confirm the above observed behavior, now modify the consistency level of few of the events to either guaranteed or one and only one.

imm1

Now again raise the CreateOrder event and observe flow trace shown below. We don’t see any change in behavior from previous iteration. So if an exception is thrown by any one of the immediate consistency level subscribers, then remaining subscribers will not get the event at all irrespective of their subscription consistency levels. And also we would see the exception thrown back while executing from EM console.

imme2

exceptionimmedaite

Consistency Level – guaranteed:

Modify all the event subscriptions to guaranteed consistency level.

gua1

Now raise the CreateOrder event and observe flow trace shown below. Since consistency level is guaranteed all the subscribers have received the event though one of the subscriber is faulted. And no fault is thrown back while testing from EM console as we saw in ‘immediate’ . Also no retry happened in case of faulted subscriber and the event is lost for that subscriber.

gua2

Also we can’t find this event in event recovery console which is shown below:

immediaterecover

Consistency Level – one and only one:

Modify all the event subscriptions to one and only one consistency level.

oaoo1

Now raise the CreateOrder event and observe flow trace shown below. Since consistency level is ‘one and only one’ all the subscribers have received the event though one of the subscriber is faulted. And no fault is thrown back while testing from EM console as we saw in ‘immediate’ . Also retry happened thrice in case of faulted subscriber as we configured the NumberOfRetrys property to 3 (see Configuration section).

oaoo2

If event delivery is failed even after the configured number of retries that event will be marked for Recovery and will be shown in Faults section of Business Events screen in EM console as shown below. Use ‘Retry’ button to recover the event delivery or use ‘Abort’ button to discard.

oaoo3

Other Observations:

Scenario 1: Mediator routes the request to Sync BPEL which throws fault and not caught, then event retry is happening irrespective of the value set for property bpel.config.transaction.

obs1

Scenario 2: Mediator routes the request to ASync BPEL which throws fault and not caught, then event retry is not happened as expected, as the fault in Async BPEL process will not propagated back unless we do it by explicit callback and bpel.config.transaction is irrelevant in this case and the value of property bpel.config.oneWayDeliveryPolicy is set to async.persist.

obs2

Scenario 3: Mediator routes the request to ASync BPEL which throws fault and not caught having the value of property bpel.config.oneWayDeliveryPolicy is set to sync. As expected, the event retry happened in this case.

obs3

The same behavior isobserved, when BPEL is subscribed to event (no mediator in between) and the value of property bpel.config.oneWayDeliveryPolicy is set to sync.

obs31

Scenario 4: Event is published and one of the subscribers is not active. In this case, non-active subscriber will loose event and no event delivery happens even after bringing up the subscriber. We will simulate this case using by doing shutdown of another composite. In the below screenshot, we could see that event is not delivered to 6th subscriber as the composite is down and the flow trace will not change even after bringing up the composite.

obs4

This is due to fact that list of subscriptions no longer has this entry as shown below, so one should think about this type of possibility in real time use cases and have contingency plan in place for such cases.

obs5

References:

http://docs.oracle.com/cd/E28280_01/dev.1111/e10224/obe_intro.htm#CHDIBHBE

Download:

Sample code used in this post can be downloaded from here.

Auto Recovery BPEL Use Case

Having seen ‘Auto Recovery’ feature in previous post, I tried the following use case to observe the behavior as we have some BPEL processes that follow this pattern. An asynchronous BPEL process (BPELA) calls synchronous BPEL (BPELB) having mid dehydration point and both of these BPEL processes don’t have the fault handlers.

BPELA:

BPEL A

BPELB:

bpel b

Flow Trace:

flow trace

If we observe, the auto recovery is happening for Sync BPEL from dehydration point and also the entire flow. This is because of the fault is not caught either in caller or calle which was propagated to runtime. One should be careful about such scenario if we are relying on auto recovery. This can be avoided by using reply activity to send the fault from sync process back to caller.

Auto Recovery feature in BPEL

Most of us are well versed with Fault Management Framework in 11g, where one of the generic feature that we implement is Retry mechanism. Recently I heard about the feature ‘Auto Recovery’ in BPEL and was a part of discussion to conclude when we should (not) rely on this feature during BPEL process execution. Actually this was a new feature for me as I ever explored and considered during the development though I heard of manual recovery. This made me realize that I am still novice :).

So the purpose of this post is to explore ‘Auto Recovery’ in BPEL that include the following. And does not discuss about required configuration in clustered environment, startup related configuration and Callback Recovery.

  • Configuration
  • BPEL Recovery Console
  • Auto Recovery Behavior
  • Auto Recovery in Action
    • Invoke
    • Activity

Configuration:

‘Auto Recovery’ configuration is done by setting few of the MBean properties in EM console. To configure it in EM console one should navigate to soa-infra -> SOA Administration -> BPEL Properties -> More BPEL Configuration Properties -> RecoveryConfig. This will bring up the following screen showing the default parameters. BPEL Auto recovery is enabled by default.

RecoveryConfig

The properties startWindowTime and stopWindowTime specify the period during which Auto Recovery is active. By default auto recovery feature will be active from 12AM to 4AM everyday (remember that it’s SOA server time), shown in above screenshot. We can change these settings by simply updating the time values in 24 hr format and do click on Apply.

The property maxMessageRaiseSize specifies the number of messages to be sent in each recovery attempt, in effect resembles the batch size.

The property subsequentTriggerDelay specifies interval between consecutive auto recovery attempts and the value is 300 sec by default.

The property threshHoldTimeInMinutes is used by BPEL engine, to mark particular instance eligible for auto recovery once the recoverable fault occurs which is 10 min by default.

If we observe closely, none of these properties mention about number of recovery attempts to be made which is altogether a separate MBean property. To set, navigate to soa-infra -> SOA Administration -> BPEL Properties -> More BPEL Configuration Properties -> MaxRecoverAttempt. The default value is 2.

RecoveryAttempt

To disable ‘Auto Recovery’, set the maxMessageRaiseSize property value to 0.

BPEL Recovery Console:

Navigate to soa-infra -> Service Engines -> BPEL -> Recovery to view the recoverable instances. Note that, the console shows all recoverable instances irrespective of enabled/disabled ‘Auto Recovery’. We can manually recover the  faulted instances from this console when Auto recovery is not enabled.

recoveryconsole

Auto Recovery Behavior:

Whenever a recoverable fault (this term is more abstract, I verified this behavior with Remote, Binding and User Defined Faults) occurs during the BPEL processing, it will be visible in Recovery console. If Auto Recovery is enabled, after threshHoldTimeInMinutes BPEL runtime will try to auto recover the instance. If it’s not successful, again number of recovery attempts will be made as given for MaxRecoverAttempt with an interval as given for subsequentTriggerDelay. If instance fails even after these maximum recover attempts, the instance will be marked as exhausted (can be queried on recovery console using message state as exhausted). We can use ‘Reset’ button to make these instances eligible for Auto Recovery again.

Note that, we observe this behavior only when the fault is thrown back to BPEL runtime or fault is not caught within BPEL process.

Auto Recovery in Action:

Developed a simple one-way BPEL process for demonstration. This BPEL has invoke activity that results in RemoteFault and dehydrate activity after that.

Scenarios Verified:

  • No Catch -> Got Remote Fault -> Auto Recovery happened.
  • Catch All -> Got Remote Fault -> Auto Recovery did not happen.
  • Catch All (Scope level) -> Got Remote Fault -> Re-throw Remote Fault -> Auto Recovery happened.
  • Catch All (Scope level) -> Got Remote Fault -> Re-throw User Defined Fault -> Auto Recovery happened.
  • Catch All (Scope level) -> Got Binding Fault -> Re-throw User Defined Fault -> Auto Recovery happened.
  • Catch All (Scope level) -> Got User Defined Fault -> Re-throw User Defined Fault -> Auto Recovery happened.

Configuration Used:

startWindowTime – 0.00

stopWindowTime – 7.00

maxMessageRaiseSize – 50

subsequentTriggerDelay – 300 (sec)

threshHoldTimeInMinutes – 5 (min)

MaxRecoverAttempt – 4

Invoke Auto Recovery in Action:

The instance is faulted with remote fault.

invoke1

The BPEL process instance is visible in Recovery console as ‘Undelivered’.

invoke2

Observed that, ‘BPEL Message Recovery Required’ notification is visible after expiration of time as given for the property threshHoldTimeInMinutes.

invoke3

After the first auto recovery attempt made by BPEL engine. Observe that retry happened by initiating process from the start as there is no dehydration point before faulted invoke.

invoke4

After the 2nd recovery attempt. Observe the time difference between the successive recovery attempts.

invoke5

After the 4 the and final recovery attempt.

invoke6

Now this BPEL process can be seen in recovery console with message state as ‘Exhausted’ (shown below) as all the 4 recovery attempts are done. Now we can recover this BPEL process manually by clicking on ‘Recover’ button or click on ‘Reset’ button to make this process eligible for auto recovery again.

invoke7

Clicking on Reset button which makes this process to be eligible for auto recovery again and BPEL engine will restart recovery attempts (shown below).

invoke8

Activity:

To demonstrate Activity auto recovery, modify BPEL process to add Dehydrate and Assign activity before faulted invoke. This case also demonstrates that auto recovery will happen from the last break point. The highlighted part shown below shows the difference from the previous scenario with Dehydrate activity along with remote fault at invoke activity level.

act1

In BPEL recovery console, we can search for the activities that are marked for recovery. Assign3 is the first activity after the dehydrate activity so the recovery should happen from this activity.

act2

Following screenshots show flow trace after the first auto recovery attempt made by BPEL engine. Observe the difference from previous run in this flow trace. Now the entire BPEL process is not started rather it starts from Assign 3 activity as expected.

act3

act4

After the 4 the recovery attempt.

act5

act6

act7

Now this BPEL process can be seen in recovery console with message state as ‘Exhausted’ (shown below) as all the 4 recovery attempts are done. Now we can recover this BPEL process manually by clicking on ‘Recover’. Observe that reset button is not available and it needs a manual recovery.

act8

Other Observations:

  • The above mentioned behavior is observed only for ASync BPEL and for Sync BPEL processes (Transient Sync) no auto recovery is performed. However, the same is not verified in case of Durable Sync BPEL processes for the time being.

 

Sample code can be downloaded from here.

References:

http://docs.oracle.com/cd/E17904_01/integration.1111/e10226/bp_config.htm

Sending JSON payload requests from BPEL

In this blog post, I am sharing sample code that demonstrates the usage of JSON in BPEL context. The code is refactored into 2 projects containing the java utility code and  a sample BPEL process. The code can be downloaded from here.

Demonstrates:

Java Utility Code:

  • Using cookies to pass authentication info (username and password).
  • Sending XML payload as URL parameters using intermediate conversion to JSON for GET request.
  • Sending HTTP request through HTTP proxy.
  • Sending JSON or XML payload in the POST request.

BPEL Process:

  • Using BPEL preferences to store any configurable information.
  • Using java embedding activity to call the external java code. The jar file has to be kept in SCA-INF/lib to refer to java code.
  • Using Replay activity. The fault policy mechanism can also be used for retry functionality, if we throw BPEL related faults from java code.
  • Converting JSON to XML and vice-versa using java utility routines in BPEL.
  • An example on creating XSD similar to JSON data format representing the service response so that XPath expressions can be used in BPEL.

Does not cover:

  • HTTP Basic Authentication
  • Using XML APIs (like JAXP) to convert input XML to URL parameters
  • URL encoding while calling HTTP GET method
  • Using JAX-RS to invoke REST based services

FYI:

  • XMLToJSONUtil.java in the code zip file is taken from Biemond’s blog.
  • The libraries used for JSON processing can be downloaded from here .
  • JSR – 353 Java API for JSON Processing is closed for public review.

The code is uploaded for reference and  this may not be the only way to achieve this.

Query to find BPEL execution time

I had a scenario where business event is captured by a mediator in one of the composites and routes it to several BPEL processes in a sequence to complete particular business flow. I had to know the time taken by each of these BPELs in my end-to-end flow. Instead of going through each of the BPEL instance in EM console, I came up with the following query to find out the time taken. I used the ECID to correlate all the BPEL instances generated during my process flow. Check if it’s of help to any.

SELECT INSTANCE_KEY, COMPOSITE_NAME, COMPONENT_NAME,
       CREATION_DATE, MODIFY_DATE, EVAL_TIME
FROM (SELECT INSTANCE_KEY, COMPOSITE_NAME, COMPONENT_NAME,
               CREATION_DATE, MODIFY_DATE, EVAL_TIME
       FROM BPEL_PROCESS_INSTANCES
        WHERE instance_key IN (SELECT cikey FROM CUBE_INSTANCE
                              WHERE ecid = <<ecid>>)
       UNION ALL
       SELECT 1 INSTANCE_KEY, ‘Comp’ COMPOSITE_NAME, COMPONENT_NAME,
             CREATED_TIME CREATION_DATE, UPDATED_TIME MODIFY_DATE,
             CASE
          WHEN UPDATED_TIME IS NOT NULL THEN
            (extract(DAY FROM (UPDATED_TIME – CREATED_TIME)) * 24 * 60 * 60 +
             extract(hour FROM (UPDATED_TIME – CREATED_TIME)) * 60 * 60 +
             extract(minute FROM (UPDATED_TIME – CREATED_TIME)) * 60 +
             extract(second FROM (UPDATED_TIME – CREATED_TIME))) * 1000
         ELSE NULL
             END AS eval_time
        FROM MEDIATOR_INSTANCE
    WHERE ecid = <<ecid>>
       )
ORDER BY INSTANCE_KEY;

In case of the multiple events, we can rewrite the query if the composites are using sensors. The following query has been used in our case to monitor and to get execution times, minimum and maximum time taken for each of the component in process flow.

/*SELECT ECID, INSTANCE_KEY, CID, COMPOSITE_NAME, COMPONENT_NAME, CREATION_DATE,  MODIFY_DATE, EVAL_TIME */
–SELECT COMPONENT_NAME,  min(EVAL_TIME), max(eval_time)
SELECT ECID, count(component_name) cnt
FROM (SELECT ECID, CIKEY INSTANCE_KEY, CIKEY CID, COMPOSITE_NAME, COMPONENT_NAME, CREATION_DATE, MODIFY_DATE,
        CASE
        WHEN MODIFY_DATE IS NOT NULL
        THEN (extract(DAY FROM (MODIFY_DATE – CREATION_DATE)) * 24 * 60 * 60 +
              extract(hour FROM (MODIFY_DATE – CREATION_DATE)) * 60 * 60 +
              extract(minute FROM (MODIFY_DATE – CREATION_DATE)) * 60 +
              extract(second FROM (MODIFY_DATE – CREATION_DATE))) * 1000
          ELSE NULL
              END AS EVAL_TIME
      FROM CUBE_INSTANCE
      WHERE ecid IN (SELECT b.ecid FROM COMPOSITE_SENSOR_VALUE a, MEDIATOR_INSTANCE b
                                               WHERE sensor_name = <<sensor name>>
                            AND  a.composite_instance_id = b.composite_instance_id
                                        AND string_value IN (<<values>>)
                        )                           
    UNION ALL
    SELECT a.ECID, 1 INSTANCE_KEY, a.composite_instance_id CID,
               ‘Comp’ COMPOSITE_NAME, a.COMPONENT_NAME,
              a.CREATED_TIME CREATION_DATE, a.UPDATED_TIME MODIFY_DATE,
              CASE
        WHEN UPDATED_TIME IS NOT NULL
        THEN (extract(DAY FROM (UPDATED_TIME – CREATED_TIME)) * 24 * 60 * 60 +
              extract(hour FROM (UPDATED_TIME – CREATED_TIME)) * 60 * 60 +
              extract(minute FROM (UPDATED_TIME – CREATED_TIME)) * 60 +
              extract(second FROM (UPDATED_TIME – CREATED_TIME))) * 1000
          ELSE NULL
              END AS eval_time
      FROM MEDIATOR_INSTANCE a, COMPOSITE_SENSOR_VALUE b
      WHERE a.composite_instance_id = b.composite_instance_id
        AND b.sensor_name = <<sensor name>>
        AND b.string_value IN (<<values>>)
  )
–GROUP BY COMPONENT_NAME
GROUP BY ECID order by cnt

–ORDER BY ecid, INSTANCE_KEY, CID;

Note: Based on the requirement, the above select and group by statements have to be commented/uncommented.

SOA Project Compilation Issue

Recently we came across some compilation issue with SOA composite. Though it’s a small issue, I feel worth sharing as we often forget few basic things during development.

The SOA composite uses DB Adapter for polling the database. Every time we refresh or run through the database wizard and compiles the composite, we are getting compilation error saying ‘variable <<variable name>> of type <<variable type>> is not defined’.

Reason: We will have a WSDL represents the service interface exposed by the DB adapter even when we used the polling option. Whenever we define a variable in BPEL the corresponding XSD (where the elements are defined) will be imported or included into this service WSDL. These import/include statements are getting removed in the WSDL every time we refresh the DB adapter. Re-adding these import/include statements in the WSDL had resolved the issue. This also applies to other kind of service interfaces

We observed the same issue with some of SOA composites after migrating from 10g to 11g.

HTTP Binding In BPEL- Testing from SOAP UI

Recently, we came across a requirement of exposing the BPEL service to be accessible over HTTP without exposing it as SOAP service. We have used the HTTP binding for this purpose. This blog provides the useful information on coming up with actual HTTP url to be used for testing as we faced issues with testing the process from EM console.

Basically the url should be some thing like below for GET invocation:

<<url given in EM>>/?<your query string>&operationName=<get operation name>

Just wanted to add simple update on this topic, the URL is working even by giving different name for operation other than default operation name ‘Request-Response’. The same has been tested from SOAP UI (GET method invocation).


Advertisements

Pages

Enter your email address to subscribe to this blog and receive notifications of new posts by email.

Join 337 other followers

Enter your email address to follow this blog and receive notifications of new posts by email.