Flask project structure for grunt based workflow - flask

I recently purchased an HTML/CSS/Js admin template based on the Bootstrap framework. It basically covered all my needs for an MVP and my plan was to customize it a bit and then plug my already developed back-end through flask.
I am quite inexperienced in this field, so I was quite impressed by the automatic workflow that was used by this admin template.
The basic structure is the following one:
root/
├── dist/
│ └── html/
│ ├── assets/
│ └── all_pages.html
├── grunt/
│ └── tasks/
├── node_modules/
├── src/
│ ├── assets/
│ ├── html/
│ ├── js/
│ └── sass/
├── Gruntfile.js
└── package.json
Thanks to grunt tasks and npm management, handling assets is very easy, after an npm install you can handle everything with grunt.
The sass are compiled in a css style for production and all code gets minified and copied to the dist folder depending on the settings.
You can easily develop on the src path, and use the grunt task "server" to both watch for changes and directly display them before sending everything to production folder "dist".
My problems arise when I try to keep this behavior with a flask application interacting with it.
My flask application uses this structure:
root/
├── __init__.py
├── templates/
│ ├── layout.html
│ └── bp1/
│ │ ├── layout.html
│ │ └── other_pages.html
│ └── bp2/
│ ├── layout.html
│ └── other_pages.html
├── views/
│ ├── __init__.py
│ ├── bp1.py.py
│ └── bp2.py.py
├── static/
│ ├── css/
│ ├── js/
│ └── img/
├── Dockerfile
└── requirements.txt
Basically, there is no difference between development and production version, and the web-app gets deployed through its docker image.
My question here is, how on earth should I approach the merging of these two guys? How to have a flask project with src-dist separation and a workflow similar to the one I described above?
I would like to keep all the good features (I managed to notice with my skills) of the admin template and have something with:
src and dist folders separation...so that all sass items, unused/discarded js code and html pages are only in the development "src" folder and will not be used in production
grunt automation for compiling sass, cleaning lib directories, watching for changes, npmcopy (to install packages with npm and move only required files to production), notifications, minification, etc...
Docker image based deployment that is based only on the "dist-generated" resource and ignores the "src-development" stuff.

Alright, I came up with a setup that works pretty neatly and that I think is worth sharing for anyone else stuck or doubtful when in a similar scenario.
Structure
root/
├── src/
│ ├── __init__.py
│ ├── models.py
│ ├── database.py
│ ├── static/
│ │ ├── css/
│ │ │ └── app.css
│ │ ├── js/
│ │ ├── img
│ │ └── lib
│ ├── templates/
│ │ ├── layout.html
│ │ ├── bp1/
│ │ │ ├── layout.html
│ │ │ └── other_pages.html
│ │ └── bp2/
│ │ ├── layout.html
│ │ └── other_pages.html
│ ├── views/
│ │ ├── __init__.py
│ │ ├── bp1.py
│ │ └── bp2.py
│ └── sass/
├── dist/
│ ├── __init__.py
│ ├── models.py
│ ├── database.py
│ ├── static/
│ │ ├── css/
│ │ │ └── app.css
│ │ ├── js/
│ │ ├── img
│ │ └── lib
│ ├── templates/
│ │ ├── layout.html
│ │ ├── bp1/
│ │ │ ├── layout.html
│ │ │ └── other_pages.html
│ │ └── bp2/
│ │ ├── layout.html
│ │ └── other_pages.html
│ └── views/
│ ├── __init__.py
│ ├── bp1.py
│ └── bp2.py
├── templates/
│ ├── layout.html
│ └── bp1/
│ │ ├── layout.html
│ │ └── other_pages.html
│ └── bp2/
│ ├── layout.html
│ └── other_pages.html
├── views/
│ ├── __init__.py
│ ├── bp1.py.py
│ └── bp2.py.py
├── static/
│ ├── css/
│ ├── js/
│ └── img/
├── instance/
│ └── flask.cfg
├── grunt/
│ └── tasks/
├── static/
├── node_modules/
├── venv/
├── Gruntfile.js
├── package.json
├── Dockerfile
├── .gitignore
└── requirements.txt
Workflow
packages are installed with npm and the package.json (node_modules gets generated).
a python virtualenv is configured using the 'requirements.txt' and linked to 'venv'.
a grunt tasks is called and uses npmcopy to move only the required files to src/static/lib that gets used by flasks' templates as: static/lib in order to keep src-dist compatibility.
a grunt task is able to compile sass parts and create 'app.css' within static/css.
several other grunt tasks do other useful things like minification.
grunt's default task performs concurrently a 'watch task' and launches flask run to let the development continue smoothly (more on this later).
grunt dist creates in the dist folder a production-ready flask project with all packages, styles and pages developed in the previous steps.
Grunt's flask task
This simple piece of code manages to launch a flask server locally to start development.
// Launch flask's server
grunt.registerTask('flask', 'Run flask server.', function() {
var spawn = require('child_process').spawn;
grunt.log.writeln('Starting Flask.');
var PIPE = {
stdio: 'inherit',
env: {
FLASK_APP: './src/__init__.py:create_app()',
FLASK_ENV: 'development',
LC_ALL: 'C.UTF-8',
LANG: 'C.UTF-8'
}
};
// more on venv later
spawn('venv/bin/flask', ['run'], PIPE);
});
Flask setup for development
In order for the flask run command to work properly in development mode, the following are configured:
venv: symbolic link to the python virtualenv used for the project.
instance/flask.cfg: flask instance folder
Gitignore
Other than the whole 'dist' folder, these are excluded from VCS:
venv;
instance folder;
lib folder within the src one;
node_modules;
Conclusion
This setup is pretty handy and is quite easy to share. Local, easy, configurations let everything work neatly for development.
Production code can be generated and then deployed/configured quickly depending on the strategies (k8s, server deployments, ...).

Related

ModuleNotFoundError: No module named 'django.config' when running test in Django 4.1

I have a django (v4.1) project with no tests defined. Before I started writing tests, I ran python manage.py test, expecting to get something like:
Found 0 test(s).
System check identified no issues (0 silenced).
....
----------------------------------------------------------------------
Ran 0 tests in 0.000s
OK
But instead I got this:
Found 1 test(s).
System check identified no issues (0 silenced).
E
======================================================================
ERROR: django.config (unittest.loader._FailedTest.django.config)
----------------------------------------------------------------------
ImportError: Failed to import test module: django.config
Traceback (most recent call last):
File "/usr/local/lib/python3.11/unittest/loader.py", line 440, in _find_test_path
package = self._get_module_from_name(name)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/usr/local/lib/python3.11/unittest/loader.py", line 350, in _get_module_from_name
__import__(name)
ModuleNotFoundError: No module named 'django.config'
----------------------------------------------------------------------
Ran 1 test in 0.000s
FAILED (errors=1)
I've checked this question but it seems more to do with a migration problem from Django 2 to 3 and how to run tests.
Clarifications:
When I say there are no tests defined, I mean that there are the auto-generated test.py files created by manage.py startapp in three applications, but I've commented out the standard import statement # from django.test import TestCase.
I tried searching for any instances of django.config using grep -r "django.config" . from the repo directory (which is above django's BASE_DIRECTORY and there are no instances of that in any of the files.
I have named the "project" directory (the one containing settings) "config" so wondered if there was come import confusion stemming from that. Did a search for all occurrences of "config":
$ grep -r "config" .
./templates/base.html: using the 'builtins' key of OPTIONS in settings (in ./config/base/templates.py).
./config/admin.py: default_site = 'config.admin.CustomAdminSite'
./config/asgi.py:ASGI config for server project.
./config/settings/base/databases.py:from config.utilities import dprint
./config/settings/base/databases.py: "NAME": str(BASE_DIR / "config/db.sqlite3"),
./config/settings/base/core.py:ROOT_URLCONF = "config.urls"
./config/settings/base/core.py:WSGI_APPLICATION = "config.wsgi.application"
./config/settings/base/authentication.py:# authall configuration settings
./config/settings/base/applications.py: # NB see ./authentication.py for social account config of allauth
./config/settings/base/applications.py: "config.admin.CustomAdminConfig", # replaces django.contrib.admin
./config/settings/__init__.py:from config.utilities import dprint
./config/settings/__init__.py:SECRETS_DIR = BASE_DIR / 'config/secrets/'
./config/wsgi.py:WSGI config for server project.
./apps/accounts/admin.py:from config.utilities import dprint
./apps/core/views.py:from config.utilities import dprint
./manage.py: os.environ.setdefault("DJANGO_SETTINGS_MODULE", "config.settings")
Project structure beneath django's BASE_DIRECTORY:
├── __init__.py
├── apps
│ ├── accounts
│ │ ├── __init__.py
│ │ ├── admin.py
│ │ ├── apps.py
│ │ ├── forms.py
│ │ ├── managers.py
│ │ ├── models.py
│ │ ├── signals.py
│ │ ├── tests.py
│ │ ├── urls.py
│ │ └── views.py
│ ├── core
│ │ ├── __init__.py
│ │ ├── admin.py
│ │ ├── apps.py
│ │ ├── models.py
│ │ ├── urls.py
│ │ └── views.py
│ └── recipes
│ ├── __init__.py
│ ├── admin.py
│ ├── apps.py
│ ├── models.py
│ ├── tests.py
│ ├── urls.py
│ └── views.py
├── config
│ ├── __init__.py
│ ├── admin.py
│ ├── asgi.py
│ ├── db.sqlite3
│ ├── secrets
│ │ ├── development.secret
│ │ └── secret.template
│ ├── settings
│ │ ├── __init__.py
│ │ ├── base
│ │ └── development
│ ├── tests.py
│ ├── urls.py
│ ├── utilities.py
│ └── wsgi.py
├── manage.py
├── static
│ ├── css
│ │ └── styles.css
│ └── js
└── templates
├── allauth
│ └── account
├── base.html
├── base_with_nav.html
├── core
│ ├── index.html
│ └── item1.html
├── navbar.html
└── navbar_rhs_items.html

How do I use a template from one app in another app

Currently trying to make a social application. I created two apps Main and posts. My post app will handle all functionality for creating, deleting a post, list of posts. My Main app is my homepage which would display all posts, for example similar to a facebook's homepage.
Main app currently authenticates users and what not. I created views and template for my posts app and I now want to include it on the Main's app homepage. Not sure how to go out about doing this. Not asking for anyone to write up the code but at least give a structure on how to achieve this.
Basic Structure of the app:
--Main_app
--_views.py
--templates
--home.html
--posts_app
--_views.py
--templates
--posts.html
One of the files my posts_app views contains currently is:
def posts_list(request):
#return HttpResponse("<h1> List a posts. </h1>")
render(requests, "posts.html", {})
templates/posts.html contains this file:
<!DOCTYPE html>
<html lang="en">
<body>
<h1>Posts template is working</h1>
</body>
</html>
And in my main_app templates/html:
{% if user.is_authenticated %}
<h1>Homepage</h1>
<!-- Posts template -->
{% include "posts/templates/posts.html" %}
{% endif %}
It made sense to me at the time to just import the template from the posts app to the main app and it would work. Did not work obviously, so I did a bit of research and couldn't find a definite solution, is there thing called templates inheritance, should import my views from posts into main. I'm not sure. Any help is appreciated.
Best way is to add a template in the project directory not in app, if you want to use that same template.
-myproject\
-Main_app\
-posts_app\
-templates\
-template_name.html
And in settings add,
TEMPLATES = [
{
'BACKEND': 'django.template.backends.django.DjangoTemplates',
'DIRS': [os.path.join(BASE_DIR, 'templates')],# Add this line
'APP_DIRS': True,
........
In your views you can refer that directly like template_name.html
Other way is to create template in the directory like
myproject\
-Main_app\
-templates\
-Main_app\
mytemplate.html
-posts_app\
Now you can use that by referring Main_app\mytemplate.html
The first one is very efficient in this case.
What you can do is place your templates folder on the root of your project directory. So instead of having
--Main_app
--_views.py
--templates
--home.html
--posts_app
--_views.py
--templates
--posts.html
You can do
--Main_app
--_views.py
--posts_app
--_views.py
--templates
--SHARED.html
--main
--home.html
--posts
--posts.html
And in your views you could do something like:
return render(request, 'SHARED.html', context) # For shared
return render(request, 'posts/posts.html', context) # For template in posts
It would be best to create a separate templates directory in your root project folder. This way you can access html files in any app you wish. Your directory structure would look something like this:
.
├── blog
│ ├── admin.py
│ ├── apps.py
│ ├── __init__.py
│ ├── migrations
│ │ ├── 0001_initial.py
│ │ ├── __init__.py
│ │ └── __pycache__
│ │ ├── 0001_initial.cpython-35.pyc
│ │ └── __init__.cpython-35.pyc
│ ├── models.py
│ ├── __pycache__
│ │ ├── admin.cpython-35.pyc
│ │ ├── apps.cpython-35.pyc
│ │ ├── __init__.cpython-35.pyc
│ │ ├── models.cpython-35.pyc
│ │ ├── urls.cpython-35.pyc
│ │ └── views.cpython-35.pyc
│ ├── tests.py
│ ├── urls.py
│ └── views.py
├── blog_project
│ ├── __init__.py
│ ├── __pycache__
│ │ ├── __init__.cpython-35.pyc
│ │ ├── settings.cpython-35.pyc
│ │ ├── urls.cpython-35.pyc
│ │ └── wsgi.cpython-35.pyc
│ ├── settings.py
│ ├── urls.py
│ └── wsgi.py
├── db.sqlite3
├── manage.py
├── Pipfile
├── Pipfile.lock
├── static
│ └── css
│ └── base.css
└── templates
├── base.html
├── home.html
├── post_detail.html
└── post_new.html
This is a simple blog I made using Django
It might be of some help if you want a reference.
As for cross loading templates via different apps, I would recommend structuring your project so that templates is a separate directory, this will benefit you in two ways:
It will be easier to totally separate front-end from back-end later on.
It will add modularity since it will decrease coupling between your apps.
Example Templates Director:
── templates
│ ├── main
│ ├── another_cool_app
│ ├── accounts
│ ├── posts
│ │ ├── timeline.html
│ │ ├── post_details.html
And so on.
For loading templates inside other templates you can simply:
{% include "posts/timeline.html" with posts_list=user.friends_posts %}
Happy coding.

Django + webpack + reactjs: how to generate multiple pages by separate reactjs code for each url

I am trying to follow:
https://medium.com/uva-mobile-devhub/set-up-react-in-your-django-project-with-webpack-4fe1f8455396
for webpack + django + reactjs
with the below directory structure
repodirectory/
├── mysite/
│ └── mysite/ # project
│ └── polls/ # app
│ └── static/
│ └── bundles/ # generated by webpack
│ └── js/
│ └── templates/
│ └── polls/
│ └── index.html/
│ └── questions.html/
│ └── manage.py
│ └── webpack-stats.json # generated by webpack
├── .babelrc
├── package.json
├── webpack.config.js
├── node_modules/ #contains our JS dependencies
I have two urls:
http://127.0.0.1:8000/polls/ -- uses index.html template
http://127.0.0.1:8000/polls/questions/ -- uses questions.html template
but i have only index.js in which i write the reactjs code
So how to write separate reactjs code for each url and then bundle it using webpack
Change
entry: './mysite/polls/static/js/index',
To
entry: {
main: './mysite/polls/static/js/index',
HomeApp: './mysite/polls/static/js/HomeApp',
},
in webpack.config.js and re-run npm run watch command

How to debug an Opsworks/Chef 11.10.4 cookbook locally on Linux (Debian9) using Centos Vagrant guest (Amazon Linux like)

I searched for this for 3 weeks but didn't find any real answer.
The main goal is to save time to test dev Chef cookbooks locally before deploying on production on AWS.
All I found is some hints using Ubuntu with Vagrant :
Chef - How to run a cookbook locally
http://pixelcog.com/blog/2015/simplify-opsworks-dev-with-packer/
Have anyone experienced to run kitchen locally with a Centos guest with a repository of Chef cookbooks with a JSON (Chef node configuration) as the node environment (like in opsworks) ?
My .kitchen.yml file and tree directory :
---
driver:
# specifies the software that manages the machine. We're using the Vagrant Test Kitchen driver
name: vagrant
provisioner:
# specifies how to run Chef. We use chef_zero because it enables you to mimic a Chef server environment on your local machine. This allows us to work with node attributes and other Chef server feature
name: chef_zero
environments_path: './env' # JSON file (node config) is not used !: env/preprod.json
client_rb:
environment: preprod
verifier:
# specifies which application to use when running automated tests. You'll learn more about automated testing in a future module.
name: inspec
platforms:
- name: centos-7
suites:
- name: default
run_list:
# list of cookbooks
- recipe[nginx::default]
attributes:
Tree without of the repository content without all files, just dir names :
(minified)
├── foobar-cookbooks
│ ├── agent_version
│ ├── apache2
│ │ └── templates
│ │ ├── default
│ ├── foobar
│ │ ├── attributes
│ │ ├── definitions
│ │ ├── recipes
│ │ └── templates
│ │ └── default
│ ├── foobar_app_akeneo
│ │ ├── definitions
│ │ ├── metadata.rb
│ │ └── templates
│ │ └── default
│ ├── foobar_app_drupal
│ │ ├── attributes
│ │ ├── definitions
│ │ ├── metadata.rb
│ │ ├── recipes
│ │ └── templates
│ ├── foobar_app_joomla
│ │ ├── attributes
│ │ ├── definitions
│ │ ├── metadata.rb
│ │ └── recipes
│ ├── Config
│ ├── dependencies
│ │ ├── attributes
│ │ ├── libraries
│ │ ├── metadata.rb
│ │ ├── recipes
│ │ └── specs
│ ├── deploy
│ │ ├── attributes
│ │ ├── definitions
│ │ ├── libraries
│ │ ├── metadata.rb
│ │ ├── specs
│ │ └── templates
│ │ └── default
│ ├── ebs
│ │ ├── attributes
│ │ ├── files
│ │ │ └── default
│ │ ├── libraries
│ │ ├── metadata.rb
│ │ ├── recipes
│ │ ├── specs
│ │ └── templates
│ │ └── default
│ ├── gem_support
│ │ ├── libraries
│ │ ├── metadata.rb
│ │ ├── recipes
│ │ └── specs
│ ├── haproxy
│ │ ├── attributes
│ │ ├── metadata.rb
│ │ ├── README.rdoc
│ │ ├── recipes
│ │ ├── specs
│ │ └── templates
│ │ └── default
│ ├── LICENSE
│ ├── memcached
│ │ ├── attributes
│ │ ├── metadata.rb
│ │ ├── recipes
│ │ ├── specs
│ │ └── templates
│ │ └── default
│ ├── mod_php5_apache2
│ │ ├── attributes
│ │ ├── metadata.rb
│ │ ├── recipes
│ │ ├── specs
│ │ └── templates
│ │ └── default
│ ├── mysql
│ │ ├── attributes
│ │ ├── files
│ │ │ └── default
│ │ ├── metadata.rb
│ │ ├── recipes
│ │ ├── specs
│ │ └── templates
│ │ └── default
│ ├── nginx
│ │ ├── attributes
│ │ ├── definitions
│ │ ├── metadata.rb
│ │ ├── specs
│ │ └── templates
│ │ └── default
│ ├── opsworks_agent_monit
│ │ ├── attributes
│ │ ├── metadata.rb
│ │ ├── recipes
│ │ ├── specs
│ │ └── templates
│ │ ├── default
│ ├── opsworks_aws_flow_ruby
│ │ ├── attributes
│ │ ├── definitions
│ │ ├── metadata.rb
│ │ ├── recipes
│ │ └── templates
│ │ └── default
│ ├── opsworks_berkshelf
│ │ ├── attributes
│ │ ├── libraries
│ │ ├── metadata.rb
│ │ ├── providers
│ │ ├── recipes
│ │ └── resources
│ ├── opsworks_bundler
│ │ ├── attributes
│ │ ├── metadata.rb
│ │ ├── recipes
│ │ └── specs
│ ├── opsworks_cleanup
│ │ ├── attributes
│ │ ├── metadata.rb
│ │ ├── recipes
│ │ └── specs
│ ├── opsworks_commons
│ │ ├── attributes
│ │ ├── definitions
│ │ ├── libraries
│ │ ├── metadata.rb
│ │ ├── providers
│ │ ├── recipes
│ │ └── resources
│ ├── opsworks_custom_cookbooks
│ │ ├── attributes
│ │ ├── libraries
│ │ ├── metadata.rb
│ │ ├── recipes
│ │ └── specs
│ ├── opsworks_ecs
│ │ ├── attributes
│ │ ├── files
│ │ │ └── default
│ │ ├── libraries
│ │ ├── metadata.rb
│ │ ├── recipes
│ │ └── templates
│ │ └── default
│ ├── opsworks_ganglia
│ │ ├── attributes
│ │ ├── files
│ │ │ └── default
│ │ ├── metadata.rb
│ │ ├── recipes
│ │ ├── specs
│ │ └── templates
│ │ └── default
│ ├── opsworks_initial_setup
│ │ ├── attributes
│ │ ├── libraries
│ │ ├── metadata.rb
│ │ ├── recipes
│ │ ├── specs
│ │ └── templates
│ │ └── default
│ ├── opsworks_java
│ │ ├── attributes
│ │ ├── metadata.rb
│ │ ├── recipes
│ │ ├── specs
│ │ └── templates
│ │ ├── amazon
│ │ ├── default
│ ├── opsworks_nodejs
│ │ ├── attributes
│ │ ├── definitions
│ │ ├── libraries
│ │ ├── metadata.rb
│ │ ├── recipes
│ │ ├── specs
│ │ └── templates
│ │ └── default
│ ├── opsworks_rubygems
│ │ ├── attributes
│ │ ├── metadata.rb
│ │ ├── recipes
│ │ └── specs
│ ├── opsworks_shutdown
│ │ ├── metadata.rb
│ │ ├── recipes
│ │ └── specs
│ ├── opsworks_stack_state_sync
│ │ ├── metadata.rb
│ │ ├── recipes
│ │ └── templates
│ │ └── default
│ ├── packages
│ │ ├── attributes
│ │ ├── libraries
│ │ ├── metadata.rb
│ │ ├── recipes
│ │ └── specs
│ ├── passenger_apache2
│ │ ├── attributes
│ │ ├── definitions
│ │ ├── metadata.rb
│ │ ├── recipes
│ │ ├── specs
│ │ └── templates
│ │ └── default
│ ├── php
│ │ ├── attributes
│ │ │ └── default.rb
│ │ ├── recipes
│ │ ├── specs
│ │ └── templates
│ │ └── default
│ ├── Rakefile
│ ├── README.md
│ ├── ruby
│ │ ├── attributes
│ │ ├── metadata.rb
│ │ ├── recipes
│ │ └── specs
│ ├── scm_helper
│ ├── ssh_host_keys
│ ├── ssh_users
│ ├── test_suite
├── attributes
├── Berksfile
├── chefignore
├── definitions
├── env
├── layers.json
├── metadata.rb
├── recipes
├── spec
├── specs
├── test
"Using Test Kitchen to Manage Test Environments
Although you can test Chef code on your private server before launching it in a production environment, node that Chef will make changes to the configuration of the server when you run the code.
A smart way to go about testing Chef code during development is to set up a sandbox environment that closely resembles a production environment. This will give you a safe place to test your Chef recipes. Chef comes with Test Kitchen, which helps you create a sandbox environment for testing. Test Kitchen utilizes Vagrant and Virtual Box to get its job done.
Test Kitchen runs on Vagrant, and you create your sandbox environment on top of Test Kitchen. Test Kitchen is installed as part of the Chef Development Kit, and you need to install it separately if you’re using Chef Client.
In order to create a virtual environment using Test Kitchen, you use the kitchen create command:
$ kitchen create default-centos65
This example shows how to create a virtual environment running CentOs. This command downloads the Vagrant base box and configure and boot the VM instance. Test Kitchen will pull base boxes that Chef Software makes available on the Internet via VagrantCloud The CentOS instance created by the last command will set a up a barebones CentOS installation with just enough stuff to let Chef run.
You can login to the CentOS VM by doing this:
$ kitchen login default-centos65
Last login: Fri May 28 10:41:48 2016
from 10.0.1.1
Welcome to your Packer-built virtual machine.
Run all your test Chef code in this Test Kitchen supported sandbox environment.
Test Kitchen uses YAML file format for its configuration files. YAML files work with two types of data – key-value pairs and lists." - Alapati, S. (March 2018). Modern Linux Administration.

how to load two webapp at one time with differenct context-name?

How can I load two webcontexts programmitically?
Here is the directory structure. and What I want is that I want to code the Starter.java programatically, not with the xml configuration.
Jetty Version : jetty-9.1.5.v20140505
├── java
│ └── com
│ └── embed
│ └── jetty
│ └── server
│ ├── Starter.java
| ...
├── resources
│ ├── log4j.properties
│ └── version.properties
└── webapps
├── webapps1
│ ├── WEB-INF
│ │ ├── classess
│ │ ├── lib
│ │ └── web.xml
│ ├── index.jsp
├── webapps2
│ ├── WEB-INF
│ │ ├── classess
│ │ ├── lib
│ │ └── web.xml
│ └─ index.jsp