> ## Documentation Index
> Fetch the complete documentation index at: https://developer.upsun.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Four days, four languages, four frameworks on Platform.sh

> Learn how to run Brightspot CMS, a Java-based content management system, on Platform.sh with Apache Tomcat, MySQL, and Solr. Deploy with ease

export const PostMeta = ({data = {}}) => {
  const {author, date, image} = data;
  const authors = Array.isArray(author) ? author : author ? [author] : [];
  const resolveAuthor = slug => {
    const entry = AUTHOR_MAP[slug] || ({});
    const name = entry.name || slug;
    const github = entry.github || null;
    const linkedin = entry.linkedin || null;
    const url = github ? `https://github.com/${github}` : linkedin || null;
    const avatarUrl = github ? `https://github.com/${github}.png?size=64` : null;
    return {
      name,
      url,
      avatarUrl
    };
  };
  const formattedDate = date ? new Date(date).toLocaleDateString('en-US', {
    year: 'numeric',
    month: 'long',
    day: 'numeric'
  }) : null;
  if (!image && authors.length === 0 && !formattedDate) return null;
  const AUTHOR_MAP = {
    "aaron-collier": {
      "name": "Aaron Collier"
    },
    "aaron-dudenhofer": {
      "name": "Aaron Dudenhofer"
    },
    "aaron-porter": {
      "name": "Aaron Porter"
    },
    "adriaan-odendaal": {
      "name": "Adriaan Odendaal"
    },
    "ajmal": {
      "name": "Ajmal Siddiqui"
    },
    "akalipetis": {
      "name": "Antonis Kalipetis"
    },
    "alexander-varwijk": {
      "name": "Alexander Varwijk"
    },
    "alicia-bevilacqua": {
      "name": "Alicia Bevilacqua"
    },
    "amelie-deguerry": {
      "name": "Amelie Deguerry"
    },
    "anacidre": {
      "name": "Ana Cidre",
      "linkedin": "https://www.linkedin.com/in/ana-cidre"
    },
    "andoni": {
      "name": "Andoni Auzmendi"
    },
    "andrei-taranu": {
      "name": "Andrei (Alex) Taranu",
      "linkedin": "https://www.linkedin.com/in/andrei-alex-taranu/"
    },
    "andrew-baxter": {
      "name": "Andrew Baxter"
    },
    "andrew-melck": {
      "name": "Andrew Melck"
    },
    "antoine-crochet-damais": {
      "name": "Antoine Crochet Damais"
    },
    "augustin-delaporte": {
      "name": "Augustin Delaporte",
      "linkedin": "https://www.linkedin.com/in/augustindelaporte/"
    },
    "branislav-bujisic": {
      "name": "Branislav Bujisic"
    },
    "carl-smith": {
      "name": "Carl Smith"
    },
    "caroline-leroy": {
      "name": "Caroline Leroy"
    },
    "cati-mayer": {
      "name": "Cati Mayer"
    },
    "catplat": {
      "name": "C Trinkwon"
    },
    "ceelolulu": {
      "name": "Celeste van der Watt"
    },
    "chadwcarlson": {
      "name": "Chad Carlson",
      "github": "chadwcarlson",
      "linkedin": "https://www.linkedin.com/in/chadwcarlson"
    },
    "chris-ward": {
      "name": "Chris Ward"
    },
    "chris-yates": {
      "name": "Chris Yates"
    },
    "christian-sieber": {
      "name": "Christian Sieber"
    },
    "christopher-lockheardt": {
      "name": "Christopher Lockheardt"
    },
    "christopher-skene": {
      "name": "Christopher Skene"
    },
    "chuck-morgan": {
      "name": "Chuck Morgan"
    },
    "corey-dockendorf": {
      "name": "Corey Dockendorf"
    },
    "crell": {
      "name": "Crell"
    },
    "damz": {
      "name": "Damz"
    },
    "dan-morrison": {
      "name": "Dan Morrison"
    },
    "davidbonachera": {
      "name": "David Bonachera",
      "github": "davidbonachera",
      "linkedin": "https://www.linkedin.com/in/davidbonachera"
    },
    "dereliahmet1": {
      "name": "Ahmet Faruk Dereli"
    },
    "devicezero": {
      "name": "Jonas Kröger",
      "github": "devicezero",
      "linkedin": "https://www.linkedin.com/in/jonaskroeger/"
    },
    "doug-goldberg": {
      "name": "Doug Goldberg"
    },
    "duncan-naves": {
      "name": "Duncan Naves",
      "github": "duncannaves",
      "linkedin": "https://www.linkedin.com/in/duncan-naves-a94423aa"
    },
    "erika-bustamante": {
      "name": "Erika Bustamante"
    },
    "fabpot": {
      "name": "Fabien Potencier"
    },
    "flovntp": {
      "name": "Florent Huck",
      "github": "flovntp",
      "linkedin": "https://www.linkedin.com/in/florenthuck"
    },
    "fred-plais": {
      "name": "Fred Plais"
    },
    "gauthier-garnier": {
      "name": "Gauthier Garnier"
    },
    "gilzow": {
      "name": "Paul Gilzow"
    },
    "gmoigneu": {
      "name": "Guillaume Moigneu",
      "github": "gmoigneu",
      "linkedin": "https://www.linkedin.com/in/guillaumemoigneu/"
    },
    "gregqualls": {
      "name": "Greg Qualls"
    },
    "guguss": {
      "name": "Augustin Delaporte"
    },
    "haylee-millar": {
      "name": "Haylee Millar"
    },
    "ivana-kotur": {
      "name": "Ivana Kotur"
    },
    "jackrabbithanna": {
      "name": "Mark Hanna"
    },
    "jared-wright": {
      "name": "Jared Wright",
      "github": "jww-sh",
      "linkedin": "https://www.linkedin.com/in/jaredwaynewright"
    },
    "jessica-orozco": {
      "name": "Jessica Orozco"
    },
    "joey-stanford": {
      "name": "Joey Stanford"
    },
    "john-grubb": {
      "name": "John Grubb"
    },
    "jonas-kruger": {
      "name": "Jonas Kruger"
    },
    "kathryn-frazer": {
      "name": "Kathryn Frazer"
    },
    "kemiojo": {
      "name": "Kemi Elizabeth Ojogbede"
    },
    "kieronsambrook-smith": {
      "name": "Kieronsambrook Smith"
    },
    "laurent-arnoud": {
      "name": "Laurent Arnoud",
      "linkedin": "https://www.linkedin.com/in/laurent-arnoud-861b44121/"
    },
    "letoya-boyne": {
      "name": "Letoya Boyne"
    },
    "lolautruche": {
      "name": "Jérôme Vieilledent"
    },
    "lyly-lepinay": {
      "name": "Lyly Lepinay"
    },
    "manauwar-alam": {
      "name": "Manauwar Alam"
    },
    "marc-antoine-porri": {
      "name": "Marc Antoine Porri"
    },
    "maria-antinkaapo": {
      "name": "Maria Antinkaapo"
    },
    "maria-de-anton": {
      "name": "Maria De Anton"
    },
    "mark-dorison": {
      "name": "Mark Dorison"
    },
    "markus-hausammann": {
      "name": "Markus Hausammann"
    },
    "mary-thomas": {
      "name": "Mary Thomas"
    },
    "mathias-bolt-lesniak": {
      "name": "Mathias Bolt Lesniak"
    },
    "mathieu-strauch": {
      "name": "Mathieu Strauch"
    },
    "matthias-van-woensel": {
      "name": "Matthias Van Woensel",
      "linkedin": "https://www.linkedin.com/in/matthias-van-woensel-267a069"
    },
    "michael-sharp": {
      "name": "Michael Sharp"
    },
    "mupsi": {
      "name": "Marine Gandy"
    },
    "natalie-harper": {
      "name": "Natalie Harper"
    },
    "ngommenginger": {
      "name": "Nicolas Gommenginger",
      "linkedin": "https://www.linkedin.com/in/nicolas-gommenginger"
    },
    "nicholas-bennison": {
      "name": "Nicholas Bennison"
    },
    "nicholas-vahalik": {
      "name": "Nicholas Vahalik"
    },
    "nick-hardiman": {
      "name": "Nick Hardiman"
    },
    "nickanderegg": {
      "name": "Nickanderegg"
    },
    "nicolas-grekas": {
      "name": "Nicolas Grekas",
      "github": "nicolas-grekas",
      "linkedin": "https://www.linkedin.com/in/nicolasgrekas/"
    },
    "niti-malwade": {
      "name": "Niti Malwade"
    },
    "opensocialteam": {
      "name": "Opensocialteam"
    },
    "ori-pekelman": {
      "name": "Ori Pekelman"
    },
    "otavio-santana": {
      "name": "Otavio Santana"
    },
    "palwandi": {
      "name": "Pawan Alwandi",
      "github": "pawpy",
      "linkedin": "https://www.linkedin.com/in/pawanalwandi"
    },
    "patrick-boest": {
      "name": "Patrick Boest"
    },
    "patrick-dawkins": {
      "name": "Patrick Dawkins",
      "github": "pjcdawkins",
      "linkedin": "https://www.linkedin.com/in/patrickdawkins"
    },
    "patrick-klima": {
      "name": "Patrick Klima"
    },
    "pjcdawkins": {
      "name": "Pjcdawkins"
    },
    "prineet-kaurbhurji": {
      "name": "Prineet Kaurbhurji"
    },
    "quentin-sinig": {
      "name": "Quentin Sinig"
    },
    "ralt": {
      "name": "Florian Margaine",
      "github": "ralt",
      "linkedin": "https://www.linkedin.com/in/florian-margaine-43971136"
    },
    "ramanathanramakrishnamurthy": {
      "name": "Ramanathanramakrishnamurthy"
    },
    "remi-lejeune": {
      "name": "Rémi Lejeune"
    },
    "ribel": {
      "name": "Taras Kruts"
    },
    "robert-douglass": {
      "name": "Robert Douglass"
    },
    "rudy-weber": {
      "name": "Rudy Weber"
    },
    "ryan-hicks": {
      "name": "Ryan Hicks"
    },
    "sabri-helal": {
      "name": "Sabri Helal"
    },
    "savannah-bergeron": {
      "name": "Savannah Bergeron"
    },
    "shannon-vettes": {
      "name": "Shannon Vettes"
    },
    "shawn-ogasawara": {
      "name": "Shawn Ogasawara",
      "linkedin": "https://www.linkedin.com/in/shawn-ogasawara-83a9a0/"
    },
    "shawna-spoor": {
      "name": "Shawna Spoor"
    },
    "shedrack-akintayo": {
      "name": "Shedrack Akintayo"
    },
    "simon-ruggier": {
      "name": "Simon Ruggier"
    },
    "sophie-van-der-kindere": {
      "name": "Sophie Van Der Kindere"
    },
    "stefanos-thampis": {
      "name": "Stefanos Thampis"
    },
    "stephen-weinberg": {
      "name": "Stephen Weinberg"
    },
    "sukhman-virk": {
      "name": "Sukhman Virk"
    },
    "sumaira-nazir": {
      "name": "Sumaira Nazir"
    },
    "sumer": {
      "name": "Sümer Cip"
    },
    "syed-raza": {
      "name": "Syed Raza"
    },
    "tamara-bacchia": {
      "name": "Tamara Bacchia"
    },
    "tara-arnold": {
      "name": "Tara Arnold"
    },
    "theosakamg": {
      "name": "Mickael Gaillard",
      "github": "theosakamg"
    },
    "thomasdiluccio": {
      "name": "Thomas di Luccio"
    },
    "tim-anderson": {
      "name": "Tim Anderson"
    },
    "tom-helmer-hansen": {
      "name": "Tom Helmer Hansen"
    },
    "tylermills": {
      "name": "Tyler Mills"
    },
    "upsun": {
      "name": "Upsun"
    },
    "veronika-tolkachova": {
      "name": "Veronika Tolkachova",
      "linkedin": "https://www.linkedin.com/in/veronika-tolkachova-169167a2"
    },
    "vince-parker": {
      "name": "Vince Parker"
    },
    "vinnie-russo": {
      "name": "Vincenzo Russo"
    },
    "vrobert78": {
      "name": "Vincent Robert",
      "github": "vrobert78",
      "linkedin": "https://www.linkedin.com/in/vincent-robert-498a883"
    },
    "yuriy-babenko": {
      "name": "Yuriy Babenko"
    },
    "yuriy-gerasimov": {
      "name": "Yuriy Gerasimov"
    }
  };
  return <div className="post-meta">
      {(authors.length > 0 || formattedDate) && <div className="post-meta-info">
          {authors.length > 0 && <div className="post-meta-authors">
              {authors.map(slug => {
    const {name, url, avatarUrl} = resolveAuthor(slug);
    const inner = <>
                    {avatarUrl && <img src={avatarUrl} alt={name} className="post-meta-avatar" />}
                    <span className="post-meta-author-name">{name}</span>
                  </>;
    return url ? <a key={slug} href={url} target="_blank" rel="noopener noreferrer" className="post-meta-author">
                    {inner}
                  </a> : <span key={slug} className="post-meta-author">{inner}</span>;
  })}
            </div>}
          {authors.length > 0 && formattedDate && <span className="post-meta-separator" aria-hidden="true">·</span>}
          {formattedDate && <span className="post-meta-date">{formattedDate}</span>}
        </div>}
      {image && <img src={image} alt="" className="post-meta-image" aria-hidden="true" />}
    </div>;
};

<PostMeta data={{ author: ["upsun"], date: "2019-02-26", image: "/images/posts/unknown/four-days-four-languages-four-frameworks-on-platformsh/four-days-four-languages-four-frameworks-on-platformsh.webp" }} />

<Tip>
  This post was originally published on the Platform.sh blog and reflects information from the time of publication.
</Tip>

Platform.sh is well-known as the premier platform for developing and delivering applications in Drupal, WordPress, Symfony, Typo3, EZ, and Magento. We've even partnered directly with [Magento](https://magento.com/products), [
Symfony](https://symfony.cloud), and [EZ Systems](https://ez.no/Products/eZ-Platform-Cloud) to help them deliver their own SaaS offerings. What you may not know is how easy it is to run *just about anything* on Platform.sh.

Our man in Bordeaux and Director of Customer Solutions Guillaume Moigneau recently went on a spree documenting how to get four new frameworks—in four different languages—up and running on Platform.sh.

The four couldn't be more different. A Java CMS and three popular static site generators (SSGs), written in Golang, React, and Ruby.

Today, we'll start with Brightspot CMS. Brightspot is a full-featured, open-source web content management system (WCMS) written in Java and backed by MySQL and Solr—two services that snap in easily to any application on Platform.sh with just a few lines of configuration.

Here, we'll get Brightspot running with Apache Tomcat, MySQL, and Solr, with a Maven build process.

As with most projects, the pattern to get the app running on Platform.sh is quite simple.

1. Check out or download a local copy of the application.
2. Add the requisite <a href="https://docs.platform.sh/overview/yaml/what-is-yaml.html">`.platform.app.yaml`</a>, `services.yaml`, and `routes.yaml` files that define the application configuration and build process, supporting data services and routing rules, respectively.
3. Push your local repo to Platform.sh.
4. For you, there's no step 4. Platform.sh takes care of <a href="/posts/unknown/what-the-heck-is-a-container">containerizing</a> your apps and services and deploying them on our grid. No CI/CD pipelines to build and manage, no Kubernetes or IaaS-specific knowledge required.

Let's skip ahead a bit to focus on a few things that are unique about running a Java/Tomcat application on Platform.sh.

## Running Tomcat

Sometimes, Java can be tricky to run on a PaaS, especially for applications that use Tomcat. But it can be done by taking advantage of Platform.sh's ability to run multiple application containers in a single project.

We've put together a ready-to-run [example repository for Brightspot](https://github.com/platformsh-examples/platformsh-example-brightspot4) that shows how to make it work. Let's go through some of the key points.

The project has two application containers, `tomcat` and `app`, each in their own directory and with their own `.platform.app.yaml` file. The Tomcat container is just a bare Java container with a few config files. Everything else happens in the [build hook](https://github.com/platformsh-examples/platformsh-example-brightspot4/blob/master/tomcat/.platform.app.yaml#L15). It's a bit long, but very straightforward; it downloads Tomcat and the Tomcat MySQL connector from scratch and installs them, then adds the config files that were included in the repository. On start, then, it runs the Catalina command to start Tomcat.

That process may not be particularly fast, but that's why it's on its own container: it only needs to run once. On subsequent deploys as long as there are no changes to the config files or Tomcat version that container won't be rebuilt, just paused momentarily. It can still be upgraded or tweaked at any time by simply modifying one of the provided config files and redeploying.

The user application lives in `app`, and contains enough information to compile the source code for the application. It doesn't run itself, though. Instead, its [build hook](https://github.com/platformsh-examples/platformsh-example-brightspot4/blob/master/app/.platform.app.yaml#L6) uses Maven to download all of the application's dependencies locally, compile the application, and then move the Maven build cache into the application. The deploy hook, then does this:

```
 # Make sure we are using the local repository.
export MAVEN_OPTS="-Dmaven.repo.local=$PLATFORM_APP_DIR/.m2/repository"
mvn \
 --offline \
 -Dtomcat.username=admin \
 -Dtomcat.password=$PLATFORM_PROJECT_ENTROPY \
 -Dmaven.tomcat.url=$(./tomcat-url)/manager/text \
 tomcat7:redeploy-only
```

What that does is install the built application into Tomcat, over the network connection between the two containers. Tomcat fully supports that sort of runtime update. The app container also has a relationship defined to let it access Tomcat:

```
relationships:
    tomcat: tomcat:http
```

Finally, the `routes.yaml` file directs all incoming HTTP requests to Tomcat:

```
https://{default}/:
  type: upstream
  upstream: tomcat:http
```

The `app` container isn't even used at all after deploy is completed! Tomcat itself, with a freshly updated copy of the compiled application, is responsible for serving all web requests.

## Brightspot-specific configuration

### Databases

We run Brightspot on top of MySQL and Solr. Two relationships are defined in the `tomcat` application's `.platform.app.yaml` file:

```yaml theme={null}
database: "mysql:mysql"
solr: "solr4:solr"
```

You can find the Brightspot services configurations in `tomcat/context.xml`. We use the `com.psddev.dari.db.AggregateDatabase` storage class with two delegates: `sql` and `solr`.

Solr is configured with the Brightspot `solrconfig.xml` and `schema.xml` located in `.platform/solr/`. (See our [Solr documentation](https://docs.platform.sh/configuration/services/solr.html) for details.)

### Example project

The example repository includes the Brightspot [hello-world](https://www.brightspot.com/documentation/) application from their documentation. Feel free to remove and replace it with your own source.

There's one quirk with the way Brightspot is configured. Maven's `mvn dependency:go-offline` sometimes has an issue installing transitive dependencies (those that are depended on by something our code depends on). For that reason the `junit`, `guava`, `findbugs`, `httpcomponents`, and `httpclient` dependencies need to be [specified explicitly](https://github.com/platformsh-examples/platformsh-example-brightspot4/blob/master/app/pom.xml#L35-L63).

### Brightspot mode

Brightspot production mode is disabled by default. You can enable it in [`tomcat/context.xml`](https://github.com/platformsh-examples/platformsh-example-brightspot4/blob/master/tomcat/context.xml#L5).

The [`isAutoCreateUser`](https://github.com/platformsh-examples/platformsh-example-brightspot4/blob/master/tomcat/context.xml#L8) option is enabled by default. Feel free to change this in the same file.

The editor default path is `cms/`.

## Starting a new project

To make trying out Brightspot on Platform.sh as easy as possible, the [template linked before](https://github.com/platformsh-examples/platformsh-example-brightspot4) is ready-to-run.

To begin a new project based on this template, follow these three simple steps:

1. Clone the repository locally. You may optionally remove the `origin` remote or remove the `.git` directory and re-init the project if you want a clean history. You'll want to commit all your local changes.

2. [Create a new project](https://auth.api.platform.sh/register?trial_type=general) through the Platform.sh user interface, and select "Import an existing project" when prompted.

   Now you can access your newly created project.

   On the wizard, click `Git remote` and copy that string.

3. Run the provided Git commands to add a Platform.sh remote, and push the code to the Platform.sh repository. It'll look like this, which you'll run inside your local Git repo.

   ```bash theme={null}
   git remote add platform <project ID>@git.<region>.platform.sh:<project ID>.git
   git push platform master
   ```

That's it! You now have a working "hello world" level project you can build on.

## Accessing the project

The `/` path defaults to a 404; that's normal since the example application has no default home page. Instead, go to `/cms/`. Until the first user is created, it will show a registration form to create the admin user, after which you'll have access to start creating pages.

The dari debug tools are accessible at `/_debug/` as long as the application is running in debug mode.

**Tomorrow** come back to check out the second post in our series about running [Hugo](https://gohugo.io), the Go-based static site generator, on Platform.sh.
